diff --git a/.github/actions/pod-install/action.yml b/.github/actions/pod-install/action.yml new file mode 100644 index 0000000000..fe721362b7 --- /dev/null +++ b/.github/actions/pod-install/action.yml @@ -0,0 +1,36 @@ +name: 'Run Cocoapods Pod Install' +description: 'This action contains steps for running pod install on a project.' +runs: + using: "composite" + steps: + - name: Select Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.4.0' + # Required for KotlinMultiplatform + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version-file: ".java-version" + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: Setup Gradle + uses: gradle/gradle-build-action@v3 + - name: Cache Cocoapods + uses: actions/cache@v4 + with: + path: ~/.cocoapods/repos + key: ${{ runner.os }}-cocoapods-${{ github.sha }} + restore-keys: ${{ runner.os }}-cocoapods- + - name: Cache Konan + uses: actions/cache@v4 + with: + path: ~/.konan + key: ${{ runner.os }}-konan-${{ github.sha }} + restore-keys: ${{ runner.os }}-konan- + - name: Pod install + shell: bash + run: bundle exec pod install --repo-update \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 48a91f8332..0000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,136 +0,0 @@ -name: Build App - -on: - push: - branches: [ develop, master, feature/* ] - pull_request: - branches: [ develop, master, feature/* ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -permissions: - id-token: write - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - runs-on: macos-14 - env: - FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 60 - - steps: - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Select Xcode Version - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '15.4.0' - - # Required for KotlinMultiplatform - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: "temurin" - java-version-file: ".java-version" - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - - - name: Setup Gradle - uses: gradle/gradle-build-action@v3 - - - name: Cache Cocoapods - uses: actions/cache@v4 - with: - path: ~/.cocoapods/repos - key: ${{ runner.os }}-cocoapods-${{ github.sha }} - restore-keys: ${{ runner.os }}-cocoapods- - - - name: Cache Konan - uses: actions/cache@v4 - with: - path: ~/.konan - key: ${{ runner.os }}-konan-${{ github.sha }} - restore-keys: ${{ runner.os }}-konan- - - - name: Pod install - run: bundle exec pod install --repo-update - - # (Levi) Disabling UITests until random crash can be resolved. "godtoolsUITests-Runner (45101) encountered an error (The test runner failed to initialize for UI testing. (Underlying Error: Timed out while loading Accessibility.))" - #- name: Run UITests - # run: bundle exec fastlane cru_shared_lane_run_tests scheme:GodTools-UITests reset_simulator:true should_clear_derived_data:true - - - name: Run Tests and Generate Code Coverage Report (.xcresult) - run: bundle exec fastlane cru_shared_lane_run_tests output_directory:fastlane_scan_output_directory result_bundle:true reset_simulator:true should_clear_derived_data:true - - - name: Upload Xcode Code Coverage Report to CodeCov - uses: codecov/codecov-action@v4 - with: - fail_ci_if_error: true - token: ${{ secrets.CODECOV_TOKEN }} - verbose: true - xcode: true - xcode_archive_path: /Users/runner/work/godtools-swift/godtools-swift/fastlane_scan_output_directory/GodTools-Production.xcresult - - - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_IAM_ONESKY_ROLE_ARN }} - aws-region: us-east-1 - - - name: Import OneSky Keys - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: dkershner6/aws-ssm-getparameters-action@v2 - with: - parameterPairs: | - /shared/onesky/PUBLIC_KEY = ONESKY_PUBLIC_KEY, - /shared/onesky/SECRET_KEY = ONESKY_SECRET_KEY - - - name: Download And Commit Latest OneSky Localizations - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - run: bundle exec fastlane cru_shared_lane_download_and_commit_latest_one_sky_localizations - - - name: Import App Store Connect API Key - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - env: - APP_STORE_CONNECT_API_JSON_PAYLOAD: ${{ secrets.APP_STORE_CONNECT_API_JSON_PAYLOAD }} - run: echo $APP_STORE_CONNECT_API_JSON_PAYLOAD > fastlane/AppleAppStoreApi.json - - - name: Increment Xcode Project Build Number - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - run: bundle exec fastlane cru_shared_lane_increment_xcode_project_build_number - - - name: Build And Deploy For TestFlight Release - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - env: - MATCH_GIT_BASIC_AUTHORIZATION_PAT: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION_PAT }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - run: bundle exec fastlane cru_shared_lane_build_and_deploy_for_testflight_release is_running_in_ci:true - - - name: Push Commits To Remote - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - run: git push - - - name: Archive XCode Logs - if: always() - uses: actions/upload-artifact@v4 - with: - name: xcode-logs - path: "/Users/runner/Library/Developer/Xcode/DerivedData/Logs/godtools-*/**" - - - name: Archive Fastlane Gym Logs - if: always() - uses: actions/upload-artifact@v4 - with: - name: fastlane-gym-logs - path: "/Users/runner/Library/Logs/gym/godtools-*/**" diff --git a/.github/workflows/create-version.yml b/.github/workflows/create-version.yml new file mode 100644 index 0000000000..ee9238ce71 --- /dev/null +++ b/.github/workflows/create-version.yml @@ -0,0 +1,238 @@ +name: Create Version + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + + workflow_dispatch: + inputs: + versionIncrementType: + description: 'Version Increment Type' + required: true + default: 'patch' + type: choice + options: + - patch + - minor + - major + - manual + manualVersionNumber: + description: 'Manually Enter Version Number (Requires manual Version Increment Type)' + required: false + type: string + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + + version_increment_is_bump: + name: Check If Version Increment Is Bump + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + outputs: + isBump: ${{ steps.check_if_increment_is_bump.outputs.value }} + steps: + - name: Check If Version Increment Is Bump + id: check_if_increment_is_bump + run: | + if [ ${{ inputs.versionIncrementType }} == 'patch' ]; then + echo "value=true" >> "$GITHUB_OUTPUT" + elif [ ${{ inputs.versionIncrementType }} == 'minor' ]; then + echo "value=true" >> "$GITHUB_OUTPUT" + elif [ ${{ inputs.versionIncrementType }} == 'major' ]; then + echo "value=true" >> "$GITHUB_OUTPUT" + else + echo echo "value=false" >> "$GITHUB_OUTPUT" + fi + + increment_version: + name: Fastlane Increment Version + runs-on: macos-14 + needs: [ version_increment_is_bump ] + if: ${{ needs.version_increment_is_bump.outputs.isBump == 'true' || inputs.versionIncrementType == 'manual' }} + steps: + - name: Increment Version + run: | + if [ ${{ needs.version_increment_is_bump.outputs.isBump }} == 'true' ]; then + bundle exec fastlane cru_shared_lane_increment_version_number bump_type:${{ inputs.versionIncrementType }} + elif [ ${{ inputs.versionIncrementType }} == 'manual' ]; then + bundle exec fastlane cru_shared_lane_increment_version_number version_number:${{ inputs.manualVersionNumber }} + else + echo "No Version" + fi + + current_version: + name: Store Current Version + runs-on: ubuntu-latest + needs: [ increment_version ] + if: github.event_name == 'workflow_dispatch' + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Set Version Output + id: version + run: grep -m 1 MARKETING_VERSION godtools.xcodeproj/project.pbxproj | sed 's/s.//' | sed "s/'//g" | sed 's/ //g' | sed 's/;//g' | sed 's/MARKETING_VERSION=//g' >> $GITHUB_OUTPUT + + print_current_version: + name: Print Current Version + runs-on: ubuntu-latest + needs: [ current_version ] + steps: + - name: Print Current Version + env: + VERSION: ${{ needs.current_version.outputs.version }} + run: | + printf '%s\n' "$VERSION" + + # current_version: + # name: Store Current Version + # runs-on: ubuntu-latest + # if: github.event_name == 'workflow_dispatch' + # outputs: + # version: ${{ steps.version.outputs.version }} + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - name: Set Version Output + # id: version + # run: grep s\\.version\\s OAuth.podspec | sed 's/s.//' | sed "s/'//g" | sed 's/ //g' >> $GITHUB_OUTPUT + + # print_current_version: + # name: Print Current Version + # runs-on: ubuntu-latest + # needs: [ current_version ] + # steps: + # - name: Print Current Version + # env: + # VERSION: ${{ needs.current_version.outputs.version }} + # run: | + # printf '%s\n' "$VERSION" + + # bump_version: + # name: Store Bump Version + # runs-on: ubuntu-latest + # outputs: + # version: ${{ steps.version.outputs.version }} + # needs: [ current_version, version_increment_is_bump ] + # steps: + # - name: Bump Version + # if: ${{ needs.version_increment_is_bump.outputs.isBump == 'true' }} + # id: bump-semver + # uses: actions-ecosystem/action-bump-semver@v1 + # with: + # current_version: ${{ needs.current_version.outputs.version }} + # level: ${{ inputs.versionIncrementType }} + # - name: Store Bump Version + # id: version + # run: | + # echo "version=${{ steps.bump-semver.outputs.new_version }}" >> $GITHUB_OUTPUT + + # print_bump_version: + # name: Print Bump Version + # runs-on: ubuntu-latest + # needs: [ bump_version, version_increment_is_bump ] + # if: ${{ needs.version_increment_is_bump.outputs.isBump == 'true' }} + # steps: + # - name: Print Bump Version + # env: + # VERSION: ${{ needs.bump_version.outputs.version }} + # run: | + # printf '%s\n' "$VERSION" + + # manual_version: + # name: Store Manual Version + # runs-on: ubuntu-latest + # outputs: + # version: ${{ steps.version.outputs.version }} + # if: inputs.versionIncrementType == 'manual' + # steps: + # - name: Store Manual Version + # id: version + # run: | + # echo "version=${{ inputs.manualVersionNumber }}" >> $GITHUB_OUTPUT + + # print_manual_version: + # name: Print Manual Version + # runs-on: ubuntu-latest + # needs: [ manual_version ] + # if: inputs.versionIncrementType == 'manual' + # steps: + # - name: Print Manual Version + # env: + # VERSION: ${{ needs.manual_version.outputs.version }} + # run: | + # printf '%s\n' "$VERSION" + + # new_version: + # name: Store New Version + # runs-on: ubuntu-latest + # needs: [ version_increment_is_bump, bump_version ] + # if: ${{ needs.version_increment_is_bump.outputs.isBump == 'true' || inputs.versionIncrementType == 'manual' }} + # outputs: + # version: ${{ steps.version.outputs.version }} + # steps: + # - name: Store New Version + # id: version + # run: | + # if [ ${{ needs.version_increment_is_bump.outputs.isBump }} == 'true' ]; then + # echo "version=${{ needs.bump_version.outputs.version }}" >> $GITHUB_OUTPUT + # elif [ ${{ inputs.versionIncrementType }} == 'manual' ]; then + # echo "version=${{ inputs.manualVersionNumber }}" >> $GITHUB_OUTPUT + # else + # echo "No Version" + # fi + + # print_new_version: + # name: Print New Version + # runs-on: ubuntu-latest + # needs: [ new_version ] + # steps: + # - name: Print New Version + # env: + # VERSION: ${{ needs.new_version.outputs.version }} + # run: | + # printf '%s\n' "$VERSION" + + # create_version_branch_and_pull_request: + # name: Create Version Branch and PR + # runs-on: ubuntu-latest + # needs: [ current_version, new_version ] + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - name: Set Podspec Version to New Version + # run: | + # sed -i "s/s.version = '${{ needs.current_version.outputs.version }}'/s.version = '${{ needs.new_version.outputs.version }}'/g" OAuth.podspec + # - name: Create Version Branch and PR + # uses: peter-evans/create-pull-request@v6 + # with: + # branch: "versions/${{ needs.new_version.outputs.version }}" + # title: "Version ${{needs.new_version.outputs.version}}" + # commit-message: "Increase version to ${{needs.new_version.outputs.version}}" + + + + + # fastlane_bump_version: + # name: Fastlane Bump Version + # runs-on: macos-14 + # needs: [ version_increment_is_bump ] + # if: ${{ needs.version_increment_is_bump.outputs.isBump == 'true' }} + # steps: + # - name: Run Fastlane Bump Version + # run: bundle exec fastlane cru_shared_lane_increment_version_number bump_type:${{ inputs.versionIncrementType }} + + # fastlane_set_version: + # name: Fastlane Set Version + # runs-on: macos-14 + # needs: [ version_increment_is_bump ] + # if: ${{ needs.version_increment_is_bump.outputs.isBump == 'false' }} + # steps: + # - name: Run Fastlane Set Version + # run: bundle exec fastlane cru_shared_lane_increment_version_number version_number:${{ inputs.manualVersionNumber }} diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 0000000000..e90da4b657 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,46 @@ +name: Run Tests + +on: + push: + branches: [ develop, master, feature/* ] + pull_request: + branches: [ develop, master, feature/* ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + run_tests: + runs-on: macos-14 + env: + FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 60 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Pod Install + uses: ./.github/actions/pod-install + + - name: Run Tests and Generate Code Coverage Report (.xcresult) + run: bundle exec fastlane cru_shared_lane_run_tests output_directory:fastlane_scan_output_directory result_bundle:true reset_simulator:true should_clear_derived_data:true + + - name: Upload Xcode Code Coverage Report to CodeCov + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + xcode: true + xcode_archive_path: /Users/runner/work/godtools-swift/godtools-swift/fastlane_scan_output_directory/GodTools-Production.xcresult + + # - name: Archive XCode Logs + # if: always() + # uses: actions/upload-artifact@v4 + # with: + # name: xcode-logs + # path: "/Users/runner/Library/Developer/Xcode/DerivedData/Logs/godtools-*/**" \ No newline at end of file diff --git a/.github/workflows/testflight.yml b/.github/workflows/testflight.yml new file mode 100644 index 0000000000..a789b9ef49 --- /dev/null +++ b/.github/workflows/testflight.yml @@ -0,0 +1,46 @@ +name: Distribute To Testflight + +on: + push: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + distribute_to_testflight: + runs-on: macos-14 + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + env: + FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 60 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Pod Install + uses: ./.github/actions/pod-install + + - name: Import App Store Connect API Key + env: + APP_STORE_CONNECT_API_JSON_PAYLOAD: ${{ secrets.APP_STORE_CONNECT_API_JSON_PAYLOAD }} + run: echo $APP_STORE_CONNECT_API_JSON_PAYLOAD > fastlane/AppleAppStoreApi.json + + - name: Increment Xcode Project Build Number + run: bundle exec fastlane cru_shared_lane_increment_xcode_project_build_number + + - name: Build And Deploy For TestFlight Release + env: + MATCH_GIT_BASIC_AUTHORIZATION_PAT: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION_PAT }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + run: bundle exec fastlane cru_shared_lane_build_and_deploy_for_testflight_release is_running_in_ci:true + + - name: Archive Fastlane Gym Logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: fastlane-gym-logs + path: "/Users/runner/Library/Logs/gym/godtools-*/**" diff --git a/.ruby-version b/.ruby-version index 4772543317..619b537668 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.2 +3.3.3 diff --git a/Gemfile.lock b/Gemfile.lock index 9a9d3db627..a8294659e4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,7 +5,7 @@ GEM base64 nkf rexml - activesupport (7.1.3.3) + activesupport (7.1.3.4) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -15,25 +15,25 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.937.0) - aws-sdk-core (3.196.1) + aws-partitions (1.949.0) + aws-sdk-core (3.200.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.82.0) - aws-sdk-core (~> 3, >= 3.193.0) + aws-sdk-kms (1.87.0) + aws-sdk-core (~> 3, >= 3.199.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.151.0) - aws-sdk-core (~> 3, >= 3.194.0) + aws-sdk-s3 (1.155.0) + aws-sdk-core (~> 3, >= 3.199.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) aws-sigv4 (1.8.0) @@ -83,7 +83,7 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.3.1) + concurrent-ruby (1.3.3) connection_pool (2.4.1) declarative (0.0.20) digest-crc (0.6.5) @@ -125,7 +125,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.220.0) + fastlane (2.221.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -219,14 +219,14 @@ GEM concurrent-ruby (~> 1.0) jmespath (1.6.2) json (2.7.2) - jwt (2.8.1) + jwt (2.8.2) base64 mime-types (3.5.2) mime-types-data (~> 3.2015) - mime-types-data (3.2024.0507) - mini_magick (4.12.0) + mime-types-data (3.2024.0604) + mini_magick (4.13.1) mini_mime (1.1.5) - minitest (5.23.1) + minitest (5.24.1) molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.4.1) @@ -253,8 +253,8 @@ GEM mime-types (>= 1.16, < 4.0) netrc (~> 0.8) retriable (3.1.2) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.2.9) + strscan rouge (2.0.7) ruby-macho (2.5.1) ruby2_keywords (0.0.5) diff --git a/README.md b/README.md index d92eba153a..16532fe281 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![codecov](https://codecov.io/gh/CruGlobal/godtools-swift/branch/main/graph/badge.svg)](https://codecov.io/gh/CruGlobal/godtools-swift) + GodTools ======== diff --git a/fastlane/README.md b/fastlane/README.md index 793cb53492..a053a6d2f5 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -127,6 +127,22 @@ Commit downloaded localization files to default branch and push to remote +### ios cru_shared_lane_get_version_number + +```sh +[bundle exec] fastlane ios cru_shared_lane_get_version_number +``` + + + +### ios cru_shared_lane_increment_version_number + +```sh +[bundle exec] fastlane ios cru_shared_lane_increment_version_number +``` + + + ### ios cru_shared_lane_build_and_deploy_for_testflight_release ```sh diff --git a/godtools.xcodeproj/project.pbxproj b/godtools.xcodeproj/project.pbxproj index 0651e5ae24..f4d8ce3b88 100644 --- a/godtools.xcodeproj/project.pbxproj +++ b/godtools.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 45006D462B80EB60002E480D /* ToolListItemDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45006D452B80EB60002E480D /* ToolListItemDomainModel.swift */; }; 45006D482B80EB9B002E480D /* GetToolsRepositoryInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45006D472B80EB9B002E480D /* GetToolsRepositoryInterface.swift */; }; 45006D4A2B80EBB6002E480D /* GetToolsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45006D492B80EBB6002E480D /* GetToolsRepository.swift */; }; - 45020C562B571BC90029B9CF /* TranslatedLanguageNameRepositorySync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45020C552B571BC90029B9CF /* TranslatedLanguageNameRepositorySync.swift */; }; 4502BD3E279F3B4D000C544A /* MobileContentFlowItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4502BD3D279F3B4D000C544A /* MobileContentFlowItemView.swift */; }; 4502BD42279F3B72000C544A /* MobileContentFlowItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4502BD41279F3B72000C544A /* MobileContentFlowItemViewModel.swift */; }; 450397332B360F1B00938EE9 /* IdentifiableRealmObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450397322B360F1B00938EE9 /* IdentifiableRealmObject.swift */; }; @@ -57,6 +56,7 @@ 45070A832ACF42AD00229EDD /* AppInterfaceStringBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45070A7D2ACF42AD00229EDD /* AppInterfaceStringBarItem.swift */; }; 45070A852ACF43E900229EDD /* AppLanguageSettingsBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45070A842ACF43E900229EDD /* AppLanguageSettingsBarItem.swift */; }; 450A3A4B282D38C300FC2E14 /* TransparentModalCustomViewLayoutType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450A3A4A282D38C300FC2E14 /* TransparentModalCustomViewLayoutType.swift */; }; + 450ADC452BECFE2F00988455 /* MockAppLanguagesRepositorySync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450ADC442BECFE2F00988455 /* MockAppLanguagesRepositorySync.swift */; }; 450B7FC22B2B53A2000B9035 /* ToolSettingsToolLanguagesListTypeDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450B7FC12B2B53A2000B9035 /* ToolSettingsToolLanguagesListTypeDomainModel.swift */; }; 450D4FA72B7FF959001DD006 /* ViewToolsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450D4FA62B7FF959001DD006 /* ViewToolsUseCase.swift */; }; 450D4FA92B7FF963001DD006 /* ViewToolsDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450D4FA82B7FF963001DD006 /* ViewToolsDomainModel.swift */; }; @@ -81,10 +81,6 @@ 450DB5622AC3552700B2FE3A /* GetAppLanguagesListUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450DB5612AC3552700B2FE3A /* GetAppLanguagesListUseCase.swift */; }; 450DB5642AC3553E00B2FE3A /* GetAppLanguagesListRepositoryInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450DB5632AC3553E00B2FE3A /* GetAppLanguagesListRepositoryInterface.swift */; }; 450DB5662AC355AA00B2FE3A /* AppLanguageListItemDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450DB5652AC355AA00B2FE3A /* AppLanguageListItemDomainModel.swift */; }; - 450E64D12B570C2700A74F6D /* RealmTranslatedLanguageName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E64CD2B570C2700A74F6D /* RealmTranslatedLanguageName.swift */; }; - 450E64D22B570C2700A74F6D /* RealmTranslatedLanguageNameCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E64CE2B570C2700A74F6D /* RealmTranslatedLanguageNameCache.swift */; }; - 450E64D32B570C2700A74F6D /* TranslatedLanguageNameDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E64CF2B570C2700A74F6D /* TranslatedLanguageNameDataModel.swift */; }; - 450E64D42B570C2700A74F6D /* TranslatedLanguageNameRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E64D02B570C2700A74F6D /* TranslatedLanguageNameRepository.swift */; }; 450E64D62B570DBD00A74F6D /* BCP47LanguageIdentifier+TranslatableLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E64D52B570DBD00A74F6D /* BCP47LanguageIdentifier+TranslatableLanguage.swift */; }; 450EE28529A6648900524F64 /* ToolDeepLinkToolPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450EE27E29A6648900524F64 /* ToolDeepLinkToolPath.swift */; }; 450EE28629A6648900524F64 /* ToolPathDeepLinkParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450EE27F29A6648900524F64 /* ToolPathDeepLinkParser.swift */; }; @@ -180,6 +176,9 @@ 452486E02B07C9A5007AD932 /* GetUserToolFiltersRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452486DE2B07C9A5007AD932 /* GetUserToolFiltersRepository.swift */; }; 452486E12B07C9A5007AD932 /* StoreUserFiltersRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452486DF2B07C9A5007AD932 /* StoreUserFiltersRepository.swift */; }; 452486E82B07C9AA007AD932 /* UserToolFiltersRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452486E62B07C9AA007AD932 /* UserToolFiltersRepository.swift */; }; + 452556442C383C3F00AA0046 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 452556432C383C3F00AA0046 /* RealmSwift */; }; + 452556452C383C3F00AA0046 /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 452556432C383C3F00AA0046 /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 452556472C383CFA00AA0046 /* AppLanguageDataModelInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBC32BECFB20006ED2F3 /* AppLanguageDataModelInterface.swift */; }; 4526EDD42AEB274800D1A3D4 /* EvaluationOptionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4526EDD32AEB274800D1A3D4 /* EvaluationOptionButton.swift */; }; 4526EDD62AEB330100D1A3D4 /* OverlayNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4526EDD52AEB330100D1A3D4 /* OverlayNavigationController.swift */; }; 4529B96628933A610011F16B /* ParseTranslationManifestForRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4529B96528933A610011F16B /* ParseTranslationManifestForRenderer.swift */; }; @@ -217,6 +216,9 @@ 452F4B082A97E117003071D1 /* PageNavigationCollectionViewCenterLayoutPageAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452F4B072A97E117003071D1 /* PageNavigationCollectionViewCenterLayoutPageAttributes.swift */; }; 452F6E852B69A33C0012AF9C /* FavoritedToolsLatestToolDownloaderInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452F6E842B69A33C0012AF9C /* FavoritedToolsLatestToolDownloaderInterface.swift */; }; 452F6E872B69A3420012AF9C /* FavoritedToolsLatestToolDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452F6E862B69A3420012AF9C /* FavoritedToolsLatestToolDownloader.swift */; }; + 45300B7D2C34282200E6F97D /* LocaleLanguageNameInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45300B7C2C34282200E6F97D /* LocaleLanguageNameInterface.swift */; }; + 45300B7F2C34282900E6F97D /* LocaleLanguageRegionNameInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45300B7E2C34282900E6F97D /* LocaleLanguageRegionNameInterface.swift */; }; + 45300B812C34283200E6F97D /* LocaleLanguageScriptNameInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45300B802C34283200E6F97D /* LocaleLanguageScriptNameInterface.swift */; }; 45308EB72BDC243700A49D96 /* SpiritualConversationReadinessScaleDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45308EB62BDC243700A49D96 /* SpiritualConversationReadinessScaleDomainModel.swift */; }; 45308EB92BDC249000A49D96 /* DidChangeScaleForSpiritualConversationReadinessUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45308EB82BDC249000A49D96 /* DidChangeScaleForSpiritualConversationReadinessUseCase.swift */; }; 45308EBB2BDC250900A49D96 /* GetSpiritualConversationReadinessScaleInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45308EBA2BDC250900A49D96 /* GetSpiritualConversationReadinessScaleInterface.swift */; }; @@ -264,9 +266,6 @@ 45368A162ABB2D850028A570 /* LocaleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45368A042ABB2D840028A570 /* LocaleTests.swift */; }; 45368A182ABB2D850028A570 /* TestRealmObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45368A082ABB2D840028A570 /* TestRealmObject.swift */; }; 45368A192ABB2D850028A570 /* DeepLinkingServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45368A0A2ABB2D840028A570 /* DeepLinkingServiceTests.swift */; }; - 45368A1A2ABB2D850028A570 /* LocalizableStringsBundleLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45368A0D2ABB2D840028A570 /* LocalizableStringsBundleLoaderTests.swift */; }; - 45368A1B2ABB2D850028A570 /* LocalizableStringsRepositoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45368A0E2ABB2D840028A570 /* LocalizableStringsRepositoryTests.swift */; }; - 45368A1C2ABB2D850028A570 /* LocalizationServicesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45368A0F2ABB2D840028A570 /* LocalizationServicesTests.swift */; }; 45369AC32AFA7FA500BD10F0 /* GetToolScreenShareTutorialRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45369A9D2AFA7FA500BD10F0 /* GetToolScreenShareTutorialRepository.swift */; }; 45369AC42AFA7FA500BD10F0 /* IncrementNumberOfToolScreenShareTutorialViewsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45369A9E2AFA7FA500BD10F0 /* IncrementNumberOfToolScreenShareTutorialViewsRepository.swift */; }; 45369AC52AFA7FA500BD10F0 /* GetToolScreenShareTutorialInterfaceStringsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45369A9F2AFA7FA500BD10F0 /* GetToolScreenShareTutorialInterfaceStringsRepository.swift */; }; @@ -399,8 +398,6 @@ 4552F54B2AD4745A0075415E /* TrackActionAnalyticsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4552F5482AD4745A0075415E /* TrackActionAnalyticsUseCase.swift */; }; 4552F54C2AD4745A0075415E /* TrackExitLinkAnalyticsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4552F5492AD4745A0075415E /* TrackExitLinkAnalyticsUseCase.swift */; }; 4554205B28B17AEA00368CFE /* AttachmentDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4554205A28B17AEA00368CFE /* AttachmentDataModel.swift */; }; - 4554512528948D220072EC6E /* LanguageSettingsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4554512428948D220072EC6E /* LanguageSettingsRepository.swift */; }; - 4554512A28948D9A0072EC6E /* LanguageSettingsCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4554512928948D9A0072EC6E /* LanguageSettingsCache.swift */; }; 455582E1269F2C1000C3FF14 /* TrainingViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 455582D3269F2C1000C3FF14 /* TrainingViewFactory.swift */; }; 455582E2269F2C1000C3FF14 /* TrainingTipView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 455582D6269F2C1000C3FF14 /* TrainingTipView.xib */; }; 455582E3269F2C1000C3FF14 /* TrainingTipViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 455582D8269F2C1000C3FF14 /* TrainingTipViewType.swift */; }; @@ -1163,8 +1160,12 @@ 45CBDA092BA3930B0007DEC8 /* MockLaunchCountRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CBDA082BA3930B0007DEC8 /* MockLaunchCountRepository.swift */; }; 45CBDA0B2BA3948A0007DEC8 /* OnboardingTutorialViewedRepositoryInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CBDA0A2BA3948A0007DEC8 /* OnboardingTutorialViewedRepositoryInterface.swift */; }; 45CBDA0E2BA394FD0007DEC8 /* MockOnboardingTutorialViewedRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CBDA0D2BA394FD0007DEC8 /* MockOnboardingTutorialViewedRepository.swift */; }; - 45CBDA122BA3993F0007DEC8 /* LocalizationServicesInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CBDA112BA3993F0007DEC8 /* LocalizationServicesInterface.swift */; }; - 45CBDA142BA399750007DEC8 /* MockLocalizationServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CBDA132BA399750007DEC8 /* MockLocalizationServices.swift */; }; + 45CC42F92C38981F00B0D82B /* MockLocalizationLanguageNameRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CC42F72C38981F00B0D82B /* MockLocalizationLanguageNameRepository.swift */; }; + 45CC43002C38982800B0D82B /* MockLocaleLanguageName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CC42FA2C38982700B0D82B /* MockLocaleLanguageName.swift */; }; + 45CC43012C38982800B0D82B /* MockLocaleLanguageRegionName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CC42FC2C38982800B0D82B /* MockLocaleLanguageRegionName.swift */; }; + 45CC43022C38982800B0D82B /* MockLocaleLanguageScriptName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CC42FE2C38982800B0D82B /* MockLocaleLanguageScriptName.swift */; }; + 45CC43062C3898B300B0D82B /* LocalizationLanguageNameRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CC43032C3898B300B0D82B /* LocalizationLanguageNameRepository.swift */; }; + 45CC43072C3898B300B0D82B /* LocalizationLanguageNameRepositoryInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CC43042C3898B300B0D82B /* LocalizationLanguageNameRepositoryInterface.swift */; }; 45CCF6922B2269010017EDFE /* ToolSettingsOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CCF68F2B2269010017EDFE /* ToolSettingsOptionView.swift */; }; 45CCF6932B2269010017EDFE /* ToolSettingsOptionViewBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CCF6902B2269010017EDFE /* ToolSettingsOptionViewBackground.swift */; }; 45CCF6942B2269010017EDFE /* ToolSettingsOptionViewTitleColorStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CCF6912B2269010017EDFE /* ToolSettingsOptionViewTitleColorStyle.swift */; }; @@ -1180,9 +1181,7 @@ 45D08D762AFAC9DD00ADA673 /* CreatingToolScreenShareSessionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D08D752AFAC9DD00ADA673 /* CreatingToolScreenShareSessionView.swift */; }; 45D08D782AFACBB700ADA673 /* CreatingToolScreenShareSessionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D08D772AFACBB700ADA673 /* CreatingToolScreenShareSessionViewModel.swift */; }; 45D318F12AE6F9D300D74A87 /* LanguageCodeDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4585BF5D2AC38D1B00A6D720 /* LanguageCodeDomainModel.swift */; }; - 45D318FA2AE700A200D74A87 /* AppLanguagesCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D318F92AE700A200D74A87 /* AppLanguagesCache.swift */; }; 45D318FB2AE7020200D74A87 /* AppLanguageDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45B3F4472AC3A86100D61BFD /* AppLanguageDataModel.swift */; }; - 45D318FD2AE7031D00D74A87 /* AppLanguagesCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D318F92AE700A200D74A87 /* AppLanguagesCache.swift */; }; 45D3AAE1298AA69F0017E6DF /* VideoViewPlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D3AAE0298AA69F0017E6DF /* VideoViewPlayerState.swift */; }; 45D40C682A5315B400E8E4AE /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 45D40C672A5315B400E8E4AE /* Starscream */; }; 45D40C6B2A5315D500E8E4AE /* Fuzi in Frameworks */ = {isa = PBXBuildFile; productRef = 45D40C6A2A5315D500E8E4AE /* Fuzi */; }; @@ -1303,12 +1302,17 @@ 45E39ED327457CB5006A59E4 /* MobileContentAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E39ED027457CB5006A59E4 /* MobileContentAnimationView.swift */; }; 45E39ED427457CB5006A59E4 /* MobileContentAnimationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E39ED127457CB5006A59E4 /* MobileContentAnimationViewModel.swift */; }; 45E39EDC27457E0C006A59E4 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 45E39EDB27457E0C006A59E4 /* Lottie */; }; - 45E41B782A83D14600F886A2 /* LocalizableStringsFileType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E41B722A83D14600F886A2 /* LocalizableStringsFileType.swift */; }; - 45E41B792A83D14600F886A2 /* LocalizableStringsBundleLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E41B732A83D14600F886A2 /* LocalizableStringsBundleLoader.swift */; }; - 45E41B7A2A83D14600F886A2 /* LocalizableStringsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E41B752A83D14600F886A2 /* LocalizableStringsBundle.swift */; }; - 45E41B7B2A83D14600F886A2 /* LocaleLocalizableStringsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E41B762A83D14600F886A2 /* LocaleLocalizableStringsBundle.swift */; }; - 45E41B7C2A83D14600F886A2 /* LocalizationServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E41B772A83D14600F886A2 /* LocalizationServices.swift */; }; - 45E41B7E2A83D48600F886A2 /* LocalizableStringsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E41B7D2A83D48600F886A2 /* LocalizableStringsRepository.swift */; }; + 45E4DBB92BECFB09006ED2F3 /* SyncInvalidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBB62BECFB09006ED2F3 /* SyncInvalidator.swift */; }; + 45E4DBBA2BECFB09006ED2F3 /* SyncInvalidatorTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBB72BECFB09006ED2F3 /* SyncInvalidatorTimeInterval.swift */; }; + 45E4DBBB2BECFB09006ED2F3 /* SyncInvalidatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBB82BECFB09006ED2F3 /* SyncInvalidatorManager.swift */; }; + 45E4DBC72BECFB20006ED2F3 /* RealmAppLanguageDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBBD2BECFB20006ED2F3 /* RealmAppLanguageDirection.swift */; }; + 45E4DBC82BECFB20006ED2F3 /* RealmAppLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBBE2BECFB20006ED2F3 /* RealmAppLanguage.swift */; }; + 45E4DBC92BECFB20006ED2F3 /* RealmAppLanguagesCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBBF2BECFB20006ED2F3 /* RealmAppLanguagesCache.swift */; }; + 45E4DBCA2BECFB20006ED2F3 /* AppLanguagesApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBC12BECFB20006ED2F3 /* AppLanguagesApi.swift */; }; + 45E4DBCB2BECFB20006ED2F3 /* AppLanguageCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBC22BECFB20006ED2F3 /* AppLanguageCodable.swift */; }; + 45E4DBCC2BECFB20006ED2F3 /* AppLanguageDataModelInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBC32BECFB20006ED2F3 /* AppLanguageDataModelInterface.swift */; }; + 45E4DBCD2BECFB20006ED2F3 /* AppLanguagesRepositorySyncInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBC52BECFB20006ED2F3 /* AppLanguagesRepositorySyncInterface.swift */; }; + 45E4DBCE2BECFB20006ED2F3 /* AppLanguagesRepositorySync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E4DBC62BECFB20006ED2F3 /* AppLanguagesRepositorySync.swift */; }; 45E585222A8E5C7F005B20A6 /* ToolFilterButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5851D2A8E5C7F005B20A6 /* ToolFilterButtonView.swift */; }; 45E585262A8E77F3005B20A6 /* TwoRowHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E585252A8E77F3005B20A6 /* TwoRowHStack.swift */; }; 45E60CFF2AEAE1C300E14BEA /* ScaleValueSliderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E60CFE2AEAE1C300E14BEA /* ScaleValueSliderView.swift */; }; @@ -1339,6 +1343,7 @@ 45EA99002AFD6AB400E7EA9A /* CreatingToolScreenShareSessionDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EA98FF2AFD6AB400E7EA9A /* CreatingToolScreenShareSessionDomainModel.swift */; }; 45EB4E732989C94C00231C67 /* FullScreenVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EB4E722989C94C00231C67 /* FullScreenVideoView.swift */; }; 45EB4E762989C9D600231C67 /* Flow+VideoModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EB4E752989C9D600231C67 /* Flow+VideoModal.swift */; }; + 45EB68E12C334D84008A5FF2 /* MockLocalizationServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EB68DF2C334D84008A5FF2 /* MockLocalizationServices.swift */; }; 45EB9B7029F16CF200CA74A8 /* UIImage+CreateImageWithColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EB9B4A29F16CF200CA74A8 /* UIImage+CreateImageWithColor.swift */; }; 45EB9B7129F16CF200CA74A8 /* UIImage+ScalePresevingAspectRatio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EB9B4B29F16CF200CA74A8 /* UIImage+ScalePresevingAspectRatio.swift */; }; 45EB9B7229F16CF200CA74A8 /* UILabel+AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EB9B4D29F16CF200CA74A8 /* UILabel+AttributedString.swift */; }; @@ -1388,6 +1393,8 @@ 45F3D39D29A514D200446F69 /* OnboardingTutorialPageAnalyticsProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F3D39C29A514D200446F69 /* OnboardingTutorialPageAnalyticsProperties.swift */; }; 45F486EF2B76D501004E0E27 /* Quick in Frameworks */ = {isa = PBXBuildFile; productRef = 45F486EE2B76D501004E0E27 /* Quick */; }; 45F486F22B76D513004E0E27 /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = 45F486F12B76D513004E0E27 /* Nimble */; }; + 45F671912C3344010017C8C2 /* LocalizationServices in Frameworks */ = {isa = PBXBuildFile; productRef = 45F671902C3344010017C8C2 /* LocalizationServices */; }; + 45F671932C33440E0017C8C2 /* LocalizationServices in Frameworks */ = {isa = PBXBuildFile; productRef = 45F671922C33440E0017C8C2 /* LocalizationServices */; }; 45F71623290A12D70019B715 /* WebContentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F715C3290A12D70019B715 /* WebContentViewModel.swift */; }; 45F71625290A12D70019B715 /* CopyrightInfoWebContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F715C6290A12D70019B715 /* CopyrightInfoWebContent.swift */; }; 45F71626290A12D70019B715 /* WebContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F715C7290A12D70019B715 /* WebContent.swift */; }; @@ -1459,7 +1466,6 @@ 4F0CCC1F1EE73E9D00AE4E45 /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = 4F0CCC1E1EE73E9D00AE4E45 /* Podfile */; }; 4F2500621F0C1E8D00364FBC /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F2500611F0C1E8D00364FBC /* AdSupport.framework */; }; 4F5732971EA69CF00082035C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4F5732961EA69CF00082035C /* Assets.xcassets */; }; - 4F57329A1EA69CF00082035C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4F5732981EA69CF00082035C /* LaunchScreen.storyboard */; }; 4FAF20781F02933900C43AFC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4FAF207A1F02933900C43AFC /* Localizable.strings */; }; 8C8C44BD9B31E90E469C99FF /* Pods_godtoolsTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D487087EDFEFD1AF7B39128C /* Pods_godtoolsTests.framework */; }; B53E1698B88EF1DB21A2DD30 /* Pods_godtoolsUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E639208D24BEB8ADAAB1104 /* Pods_godtoolsUITests.framework */; }; @@ -1537,11 +1543,19 @@ D4BC79E429AE995A0040651B /* GetTrainingTipCompletedUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BC79E329AE99590040651B /* GetTrainingTipCompletedUseCase.swift */; }; D4BC79E629AFCE870040651B /* TrainingTipDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BC79E529AFCE870040651B /* TrainingTipDomainModel.swift */; }; D4BC79E829B130970040651B /* SetCompletedTrainingTipUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BC79E729B130970040651B /* SetCompletedTrainingTipUseCase.swift */; }; + D4BE3F632C08074400887BFD /* PersistUserToolLanguageSettingsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BE3F622C08074400887BFD /* PersistUserToolLanguageSettingsUseCase.swift */; }; + D4BE3F652C09029900887BFD /* PersistUserToolLanguageSettingsRepositoryInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BE3F642C09029900887BFD /* PersistUserToolLanguageSettingsRepositoryInterface.swift */; }; + D4BE3F672C09048C00887BFD /* PersistUserToolLanguageSettingsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BE3F662C09048C00887BFD /* PersistUserToolLanguageSettingsRepository.swift */; }; + D4BE3F6E2C0907E900887BFD /* RealmUserToolSettingsCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BE3F6D2C0907E900887BFD /* RealmUserToolSettingsCache.swift */; }; + D4BE3F702C0907FC00887BFD /* RealmUserToolSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BE3F6F2C0907FC00887BFD /* RealmUserToolSettings.swift */; }; + D4BE3F722C0909BA00887BFD /* UserToolSettingsDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BE3F712C0909BA00887BFD /* UserToolSettingsDataModel.swift */; }; + D4BE3F782C0E967200887BFD /* UserToolSettingsDomainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BE3F772C0E967200887BFD /* UserToolSettingsDomainModel.swift */; }; D4C14E9229411A35001465F1 /* MobileContentAuthTokenDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C14E9129411A35001465F1 /* MobileContentAuthTokenDataModel.swift */; }; D4C759212BB643EF00D6AEB2 /* SearchBarLegacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C759202BB643EF00D6AEB2 /* SearchBarLegacy.swift */; }; D4E243CC29EDC6A100EA4BB7 /* SendFeedbackWebContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E243CB29EDC6A100EA4BB7 /* SendFeedbackWebContent.swift */; }; D4E243CE29EDCA5700EA4BB7 /* ReportABugWebContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E243CD29EDCA5700EA4BB7 /* ReportABugWebContent.swift */; }; D4E243D029EDD2E800EA4BB7 /* AskAQuestionWebContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E243CF29EDD2E800EA4BB7 /* AskAQuestionWebContent.swift */; }; + D4EAF2F92C0F95D900AEC36C /* UserToolSettingsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EAF2F82C0F95D900AEC36C /* UserToolSettingsRepository.swift */; }; D4EFC9632B49B28200B0D34A /* LanguageDownloadErrorAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EFC9622B49B28200B0D34A /* LanguageDownloadErrorAlertView.swift */; }; D4EFC9652B49B29D00B0D34A /* LanguageDownloadErrorAlertViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EFC9642B49B29D00B0D34A /* LanguageDownloadErrorAlertViewModel.swift */; }; D4F0457B2BBB464D00B115C9 /* RealmUserToolFiltersCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0457A2BBB464D00B115C9 /* RealmUserToolFiltersCache.swift */; }; @@ -1578,6 +1592,17 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 452556462C383C3F00AA0046 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 452556452C383C3F00AA0046 /* RealmSwift in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 45B10ADF2BF3F270004342AB /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 12; @@ -1620,7 +1645,6 @@ 45006D452B80EB60002E480D /* ToolListItemDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolListItemDomainModel.swift; sourceTree = ""; }; 45006D472B80EB9B002E480D /* GetToolsRepositoryInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetToolsRepositoryInterface.swift; sourceTree = ""; }; 45006D492B80EBB6002E480D /* GetToolsRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetToolsRepository.swift; sourceTree = ""; }; - 45020C552B571BC90029B9CF /* TranslatedLanguageNameRepositorySync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TranslatedLanguageNameRepositorySync.swift; sourceTree = ""; }; 4502BD3D279F3B4D000C544A /* MobileContentFlowItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobileContentFlowItemView.swift; sourceTree = ""; }; 4502BD41279F3B72000C544A /* MobileContentFlowItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobileContentFlowItemViewModel.swift; sourceTree = ""; }; 450397322B360F1B00938EE9 /* IdentifiableRealmObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifiableRealmObject.swift; sourceTree = ""; }; @@ -1659,6 +1683,7 @@ 45070A7D2ACF42AD00229EDD /* AppInterfaceStringBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppInterfaceStringBarItem.swift; sourceTree = ""; }; 45070A842ACF43E900229EDD /* AppLanguageSettingsBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLanguageSettingsBarItem.swift; sourceTree = ""; }; 450A3A4A282D38C300FC2E14 /* TransparentModalCustomViewLayoutType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransparentModalCustomViewLayoutType.swift; sourceTree = ""; }; + 450ADC442BECFE2F00988455 /* MockAppLanguagesRepositorySync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockAppLanguagesRepositorySync.swift; sourceTree = ""; }; 450B7FC12B2B53A2000B9035 /* ToolSettingsToolLanguagesListTypeDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolSettingsToolLanguagesListTypeDomainModel.swift; sourceTree = ""; }; 450D4FA62B7FF959001DD006 /* ViewToolsUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewToolsUseCase.swift; sourceTree = ""; }; 450D4FA82B7FF963001DD006 /* ViewToolsDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewToolsDomainModel.swift; sourceTree = ""; }; @@ -1683,10 +1708,6 @@ 450DB5612AC3552700B2FE3A /* GetAppLanguagesListUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetAppLanguagesListUseCase.swift; sourceTree = ""; }; 450DB5632AC3553E00B2FE3A /* GetAppLanguagesListRepositoryInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetAppLanguagesListRepositoryInterface.swift; sourceTree = ""; }; 450DB5652AC355AA00B2FE3A /* AppLanguageListItemDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLanguageListItemDomainModel.swift; sourceTree = ""; }; - 450E64CD2B570C2700A74F6D /* RealmTranslatedLanguageName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmTranslatedLanguageName.swift; sourceTree = ""; }; - 450E64CE2B570C2700A74F6D /* RealmTranslatedLanguageNameCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmTranslatedLanguageNameCache.swift; sourceTree = ""; }; - 450E64CF2B570C2700A74F6D /* TranslatedLanguageNameDataModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TranslatedLanguageNameDataModel.swift; sourceTree = ""; }; - 450E64D02B570C2700A74F6D /* TranslatedLanguageNameRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TranslatedLanguageNameRepository.swift; sourceTree = ""; }; 450E64D52B570DBD00A74F6D /* BCP47LanguageIdentifier+TranslatableLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BCP47LanguageIdentifier+TranslatableLanguage.swift"; sourceTree = ""; }; 450EE27E29A6648900524F64 /* ToolDeepLinkToolPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolDeepLinkToolPath.swift; sourceTree = ""; }; 450EE27F29A6648900524F64 /* ToolPathDeepLinkParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolPathDeepLinkParser.swift; sourceTree = ""; }; @@ -1809,6 +1830,9 @@ 452F4B072A97E117003071D1 /* PageNavigationCollectionViewCenterLayoutPageAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageNavigationCollectionViewCenterLayoutPageAttributes.swift; sourceTree = ""; }; 452F6E842B69A33C0012AF9C /* FavoritedToolsLatestToolDownloaderInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FavoritedToolsLatestToolDownloaderInterface.swift; sourceTree = ""; }; 452F6E862B69A3420012AF9C /* FavoritedToolsLatestToolDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritedToolsLatestToolDownloader.swift; sourceTree = ""; }; + 45300B7C2C34282200E6F97D /* LocaleLanguageNameInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocaleLanguageNameInterface.swift; sourceTree = ""; }; + 45300B7E2C34282900E6F97D /* LocaleLanguageRegionNameInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocaleLanguageRegionNameInterface.swift; sourceTree = ""; }; + 45300B802C34283200E6F97D /* LocaleLanguageScriptNameInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocaleLanguageScriptNameInterface.swift; sourceTree = ""; }; 45308EB62BDC243700A49D96 /* SpiritualConversationReadinessScaleDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpiritualConversationReadinessScaleDomainModel.swift; sourceTree = ""; }; 45308EB82BDC249000A49D96 /* DidChangeScaleForSpiritualConversationReadinessUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DidChangeScaleForSpiritualConversationReadinessUseCase.swift; sourceTree = ""; }; 45308EBA2BDC250900A49D96 /* GetSpiritualConversationReadinessScaleInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSpiritualConversationReadinessScaleInterface.swift; sourceTree = ""; }; @@ -1857,9 +1881,6 @@ 45368A042ABB2D840028A570 /* LocaleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocaleTests.swift; sourceTree = ""; }; 45368A082ABB2D840028A570 /* TestRealmObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRealmObject.swift; sourceTree = ""; }; 45368A0A2ABB2D840028A570 /* DeepLinkingServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkingServiceTests.swift; sourceTree = ""; }; - 45368A0D2ABB2D840028A570 /* LocalizableStringsBundleLoaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableStringsBundleLoaderTests.swift; sourceTree = ""; }; - 45368A0E2ABB2D840028A570 /* LocalizableStringsRepositoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableStringsRepositoryTests.swift; sourceTree = ""; }; - 45368A0F2ABB2D840028A570 /* LocalizationServicesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizationServicesTests.swift; sourceTree = ""; }; 45368A142ABB2D840028A570 /* MobileContentBackgroundImageRendererTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MobileContentBackgroundImageRendererTests.swift; sourceTree = ""; }; 45369A9D2AFA7FA500BD10F0 /* GetToolScreenShareTutorialRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetToolScreenShareTutorialRepository.swift; sourceTree = ""; }; 45369A9E2AFA7FA500BD10F0 /* IncrementNumberOfToolScreenShareTutorialViewsRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementNumberOfToolScreenShareTutorialViewsRepository.swift; sourceTree = ""; }; @@ -1995,8 +2016,6 @@ 4552F5482AD4745A0075415E /* TrackActionAnalyticsUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackActionAnalyticsUseCase.swift; sourceTree = ""; }; 4552F5492AD4745A0075415E /* TrackExitLinkAnalyticsUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackExitLinkAnalyticsUseCase.swift; sourceTree = ""; }; 4554205A28B17AEA00368CFE /* AttachmentDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentDataModel.swift; sourceTree = ""; }; - 4554512428948D220072EC6E /* LanguageSettingsRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageSettingsRepository.swift; sourceTree = ""; }; - 4554512928948D9A0072EC6E /* LanguageSettingsCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageSettingsCache.swift; sourceTree = ""; }; 4555599A27B3193D00A32A61 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; 4555599B27B3194800A32A61 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; 455582D3269F2C1000C3FF14 /* TrainingViewFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrainingViewFactory.swift; sourceTree = ""; }; @@ -2714,6 +2733,8 @@ 45C2AF462A817621004958AB /* PageControllAttributesType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PageControllAttributesType.swift; sourceTree = ""; }; 45C2AF492A81764D004958AB /* GTPageControlAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GTPageControlAttributes.swift; sourceTree = ""; }; 45C321A62ACE7A89003B4F2C /* AppInterfaceStringNavBarItemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInterfaceStringNavBarItemController.swift; sourceTree = ""; }; + 45C493F92C334A8E001D81F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; + 45C493FA2C334A99001D81F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = Base; path = Base.lproj/Localizable.stringsdict; sourceTree = ""; }; 45C5DDCD2AE6BF1300C70BBA /* AccessibilityScreenElementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityScreenElementView.swift; sourceTree = ""; }; 45C649BB2B31E14D000249E0 /* ViewShareToolUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewShareToolUseCase.swift; sourceTree = ""; }; 45C649BD2B31E178000249E0 /* ViewShareToolDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewShareToolDomainModel.swift; sourceTree = ""; }; @@ -2734,8 +2755,12 @@ 45CBDA0A2BA3948A0007DEC8 /* OnboardingTutorialViewedRepositoryInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTutorialViewedRepositoryInterface.swift; sourceTree = ""; }; 45CBDA0D2BA394FD0007DEC8 /* MockOnboardingTutorialViewedRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockOnboardingTutorialViewedRepository.swift; sourceTree = ""; }; 45CBDA0F2BA398D90007DEC8 /* GetOnboardingTutorialInterfaceStringsRepositoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetOnboardingTutorialInterfaceStringsRepositoryTests.swift; sourceTree = ""; }; - 45CBDA112BA3993F0007DEC8 /* LocalizationServicesInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationServicesInterface.swift; sourceTree = ""; }; - 45CBDA132BA399750007DEC8 /* MockLocalizationServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLocalizationServices.swift; sourceTree = ""; }; + 45CC42F72C38981F00B0D82B /* MockLocalizationLanguageNameRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLocalizationLanguageNameRepository.swift; sourceTree = ""; }; + 45CC42FA2C38982700B0D82B /* MockLocaleLanguageName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLocaleLanguageName.swift; sourceTree = ""; }; + 45CC42FC2C38982800B0D82B /* MockLocaleLanguageRegionName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLocaleLanguageRegionName.swift; sourceTree = ""; }; + 45CC42FE2C38982800B0D82B /* MockLocaleLanguageScriptName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLocaleLanguageScriptName.swift; sourceTree = ""; }; + 45CC43032C3898B300B0D82B /* LocalizationLanguageNameRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizationLanguageNameRepository.swift; sourceTree = ""; }; + 45CC43042C3898B300B0D82B /* LocalizationLanguageNameRepositoryInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizationLanguageNameRepositoryInterface.swift; sourceTree = ""; }; 45CCF68F2B2269010017EDFE /* ToolSettingsOptionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolSettingsOptionView.swift; sourceTree = ""; }; 45CCF6902B2269010017EDFE /* ToolSettingsOptionViewBackground.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolSettingsOptionViewBackground.swift; sourceTree = ""; }; 45CCF6912B2269010017EDFE /* ToolSettingsOptionViewTitleColorStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolSettingsOptionViewTitleColorStyle.swift; sourceTree = ""; }; @@ -2749,7 +2774,6 @@ 45D08D6F2AFABF4400ADA673 /* ToolSettingsFlowCompletedState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolSettingsFlowCompletedState.swift; sourceTree = ""; }; 45D08D752AFAC9DD00ADA673 /* CreatingToolScreenShareSessionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreatingToolScreenShareSessionView.swift; sourceTree = ""; }; 45D08D772AFACBB700ADA673 /* CreatingToolScreenShareSessionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatingToolScreenShareSessionViewModel.swift; sourceTree = ""; }; - 45D318F92AE700A200D74A87 /* AppLanguagesCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLanguagesCache.swift; sourceTree = ""; }; 45D3AAE0298AA69F0017E6DF /* VideoViewPlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoViewPlayerState.swift; sourceTree = ""; }; 45D48C8229F81082004E92B1 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; 45D48C8829F81692004E92B1 /* AppConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppConfig.swift; sourceTree = ""; }; @@ -2872,12 +2896,17 @@ 45E39EC827457A14006A59E4 /* AnimatedResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedResource.swift; sourceTree = ""; }; 45E39ED027457CB5006A59E4 /* MobileContentAnimationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MobileContentAnimationView.swift; sourceTree = ""; }; 45E39ED127457CB5006A59E4 /* MobileContentAnimationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MobileContentAnimationViewModel.swift; sourceTree = ""; }; - 45E41B722A83D14600F886A2 /* LocalizableStringsFileType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableStringsFileType.swift; sourceTree = ""; }; - 45E41B732A83D14600F886A2 /* LocalizableStringsBundleLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableStringsBundleLoader.swift; sourceTree = ""; }; - 45E41B752A83D14600F886A2 /* LocalizableStringsBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableStringsBundle.swift; sourceTree = ""; }; - 45E41B762A83D14600F886A2 /* LocaleLocalizableStringsBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocaleLocalizableStringsBundle.swift; sourceTree = ""; }; - 45E41B772A83D14600F886A2 /* LocalizationServices.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizationServices.swift; sourceTree = ""; }; - 45E41B7D2A83D48600F886A2 /* LocalizableStringsRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableStringsRepository.swift; sourceTree = ""; }; + 45E4DBB62BECFB09006ED2F3 /* SyncInvalidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncInvalidator.swift; sourceTree = ""; }; + 45E4DBB72BECFB09006ED2F3 /* SyncInvalidatorTimeInterval.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncInvalidatorTimeInterval.swift; sourceTree = ""; }; + 45E4DBB82BECFB09006ED2F3 /* SyncInvalidatorManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncInvalidatorManager.swift; sourceTree = ""; }; + 45E4DBBD2BECFB20006ED2F3 /* RealmAppLanguageDirection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmAppLanguageDirection.swift; sourceTree = ""; }; + 45E4DBBE2BECFB20006ED2F3 /* RealmAppLanguage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmAppLanguage.swift; sourceTree = ""; }; + 45E4DBBF2BECFB20006ED2F3 /* RealmAppLanguagesCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmAppLanguagesCache.swift; sourceTree = ""; }; + 45E4DBC12BECFB20006ED2F3 /* AppLanguagesApi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLanguagesApi.swift; sourceTree = ""; }; + 45E4DBC22BECFB20006ED2F3 /* AppLanguageCodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLanguageCodable.swift; sourceTree = ""; }; + 45E4DBC32BECFB20006ED2F3 /* AppLanguageDataModelInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLanguageDataModelInterface.swift; sourceTree = ""; }; + 45E4DBC52BECFB20006ED2F3 /* AppLanguagesRepositorySyncInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLanguagesRepositorySyncInterface.swift; sourceTree = ""; }; + 45E4DBC62BECFB20006ED2F3 /* AppLanguagesRepositorySync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLanguagesRepositorySync.swift; sourceTree = ""; }; 45E5851D2A8E5C7F005B20A6 /* ToolFilterButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolFilterButtonView.swift; sourceTree = ""; }; 45E585252A8E77F3005B20A6 /* TwoRowHStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwoRowHStack.swift; sourceTree = ""; }; 45E60CFE2AEAE1C300E14BEA /* ScaleValueSliderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaleValueSliderView.swift; sourceTree = ""; }; @@ -2907,6 +2936,7 @@ 45EA98FF2AFD6AB400E7EA9A /* CreatingToolScreenShareSessionDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatingToolScreenShareSessionDomainModel.swift; sourceTree = ""; }; 45EB4E722989C94C00231C67 /* FullScreenVideoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenVideoView.swift; sourceTree = ""; }; 45EB4E752989C9D600231C67 /* Flow+VideoModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Flow+VideoModal.swift"; sourceTree = ""; }; + 45EB68DF2C334D84008A5FF2 /* MockLocalizationServices.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLocalizationServices.swift; sourceTree = ""; }; 45EB9B4A29F16CF200CA74A8 /* UIImage+CreateImageWithColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+CreateImageWithColor.swift"; sourceTree = ""; }; 45EB9B4B29F16CF200CA74A8 /* UIImage+ScalePresevingAspectRatio.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+ScalePresevingAspectRatio.swift"; sourceTree = ""; }; 45EB9B4D29F16CF200CA74A8 /* UILabel+AttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+AttributedString.swift"; sourceTree = ""; }; @@ -3051,7 +3081,6 @@ 4F2500611F0C1E8D00364FBC /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; 4F5732891EA69CF00082035C /* godtools.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = godtools.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4F5732961EA69CF00082035C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 4F5732991EA69CF00082035C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 4F57329B1EA69CF00082035C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4F66E17A1F1943F9004BBEF1 /* godtools.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = godtools.entitlements; sourceTree = ""; }; 4F6FC0EC204854AB003A87DF /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; @@ -3060,7 +3089,6 @@ 4F6FC0EF204854E8003A87DF /* pa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pa; path = pa.lproj/Localizable.strings; sourceTree = ""; }; 4F6FC0F02048550E003A87DF /* ru-MD */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ru-MD"; path = "ru-MD.lproj/Localizable.strings"; sourceTree = ""; }; 4F6FC0F120485520003A87DF /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/Localizable.strings; sourceTree = ""; }; - 4FAF20791F02933900C43AFC /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; 4FC97EAA20D2B27B00084A33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4FF90B8C21E3A9F500374DCB /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/Localizable.strings; sourceTree = ""; }; 4FF90B8D21E3AA9500374DCB /* am */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = am; path = am.lproj/Localizable.strings; sourceTree = ""; }; @@ -3150,7 +3178,6 @@ D4B060012912C335005852D0 /* MobileContentAuthTokenCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobileContentAuthTokenCache.swift; sourceTree = ""; }; D4B060032913096A005852D0 /* MobileContentAuthTokenKeychainAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobileContentAuthTokenKeychainAccessor.swift; sourceTree = ""; }; D4B060062915647B005852D0 /* KeychainServiceResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainServiceResponse.swift; sourceTree = ""; }; - D4B522DA29D761B700D85213 /* English */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = English; path = English.lproj/Localizable.stringsdict; sourceTree = ""; }; D4B522DC29D761BA00D85213 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = af; path = af.lproj/Localizable.stringsdict; sourceTree = ""; }; D4B522DD29D761BA00D85213 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sq; path = sq.lproj/Localizable.stringsdict; sourceTree = ""; }; D4B522DE29D761BB00D85213 /* am */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = am; path = am.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -3224,11 +3251,19 @@ D4BC79E329AE99590040651B /* GetTrainingTipCompletedUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetTrainingTipCompletedUseCase.swift; sourceTree = ""; }; D4BC79E529AFCE870040651B /* TrainingTipDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingTipDomainModel.swift; sourceTree = ""; }; D4BC79E729B130970040651B /* SetCompletedTrainingTipUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetCompletedTrainingTipUseCase.swift; sourceTree = ""; }; + D4BE3F622C08074400887BFD /* PersistUserToolLanguageSettingsUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistUserToolLanguageSettingsUseCase.swift; sourceTree = ""; }; + D4BE3F642C09029900887BFD /* PersistUserToolLanguageSettingsRepositoryInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistUserToolLanguageSettingsRepositoryInterface.swift; sourceTree = ""; }; + D4BE3F662C09048C00887BFD /* PersistUserToolLanguageSettingsRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistUserToolLanguageSettingsRepository.swift; sourceTree = ""; }; + D4BE3F6D2C0907E900887BFD /* RealmUserToolSettingsCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmUserToolSettingsCache.swift; sourceTree = ""; }; + D4BE3F6F2C0907FC00887BFD /* RealmUserToolSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmUserToolSettings.swift; sourceTree = ""; }; + D4BE3F712C0909BA00887BFD /* UserToolSettingsDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserToolSettingsDataModel.swift; sourceTree = ""; }; + D4BE3F772C0E967200887BFD /* UserToolSettingsDomainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserToolSettingsDomainModel.swift; sourceTree = ""; }; D4C14E9129411A35001465F1 /* MobileContentAuthTokenDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobileContentAuthTokenDataModel.swift; sourceTree = ""; }; D4C759202BB643EF00D6AEB2 /* SearchBarLegacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarLegacy.swift; sourceTree = ""; }; D4E243CB29EDC6A100EA4BB7 /* SendFeedbackWebContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendFeedbackWebContent.swift; sourceTree = ""; }; D4E243CD29EDCA5700EA4BB7 /* ReportABugWebContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportABugWebContent.swift; sourceTree = ""; }; D4E243CF29EDD2E800EA4BB7 /* AskAQuestionWebContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AskAQuestionWebContent.swift; sourceTree = ""; }; + D4EAF2F82C0F95D900AEC36C /* UserToolSettingsRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserToolSettingsRepository.swift; sourceTree = ""; }; D4EFC9622B49B28200B0D34A /* LanguageDownloadErrorAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageDownloadErrorAlertView.swift; sourceTree = ""; }; D4EFC9642B49B29D00B0D34A /* LanguageDownloadErrorAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageDownloadErrorAlertViewModel.swift; sourceTree = ""; }; D4F0457A2BBB464D00B115C9 /* RealmUserToolFiltersCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmUserToolFiltersCache.swift; sourceTree = ""; }; @@ -3271,6 +3306,7 @@ files = ( 45A4161D2A9943E20030E2C7 /* AdSupport.framework in Frameworks */, 45A416182A9943D50030E2C7 /* SocialAuthentication in Frameworks */, + 452556442C383C3F00AA0046 /* RealmSwift in Frameworks */, 45A416142A9943D50030E2C7 /* FirebaseDynamicLinks in Frameworks */, 45A4160C2A9943D50030E2C7 /* YouTubeiOSPlayerHelper in Frameworks */, 45A416082A9943D50030E2C7 /* RequestOperation in Frameworks */, @@ -3290,6 +3326,7 @@ buildActionMask = 2147483647; files = ( 45D40C6B2A5315D500E8E4AE /* Fuzi in Frameworks */, + 45F671912C3344010017C8C2 /* LocalizationServices in Frameworks */, 4F2500621F0C1E8D00364FBC /* AdSupport.framework in Frameworks */, 457771B229A00C5C00EA8115 /* FirebaseAnalytics in Frameworks */, 4581EAA32BFBE194008115FF /* RealmSwift in Frameworks */, @@ -3311,6 +3348,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 45F671932C33440E0017C8C2 /* LocalizationServices in Frameworks */, 4581EAA52BFBE257008115FF /* RealmSwift in Frameworks */, 45F486F22B76D513004E0E27 /* Nimble in Frameworks */, 45F486EF2B76D501004E0E27 /* Quick in Frameworks */, @@ -3384,14 +3422,6 @@ path = Subviews; sourceTree = ""; }; - 45020C542B571BC90029B9CF /* Sync */ = { - isa = PBXGroup; - children = ( - 45020C552B571BC90029B9CF /* TranslatedLanguageNameRepositorySync.swift */, - ); - path = Sync; - sourceTree = ""; - }; 4502BD3C279F3B3C000C544A /* ContentFlowItem */ = { isa = PBXGroup; children = ( @@ -3617,6 +3647,30 @@ path = ButtonsWithTitle; sourceTree = ""; }; + 450ADC412BECFE1F00988455 /* Data */ = { + isa = PBXGroup; + children = ( + 450ADC422BECFE1F00988455 /* AppLanguagesRepository */, + ); + path = Data; + sourceTree = ""; + }; + 450ADC422BECFE1F00988455 /* AppLanguagesRepository */ = { + isa = PBXGroup; + children = ( + 450ADC432BECFE1F00988455 /* Sync */, + ); + path = AppLanguagesRepository; + sourceTree = ""; + }; + 450ADC432BECFE1F00988455 /* Sync */ = { + isa = PBXGroup; + children = ( + 450ADC442BECFE2F00988455 /* MockAppLanguagesRepositorySync.swift */, + ); + path = Sync; + sourceTree = ""; + }; 450D7AF928E32965006C3FDF /* TrackDownloadedTranslationsRepository */ = { isa = PBXGroup; children = ( @@ -3708,26 +3762,6 @@ path = Domain; sourceTree = ""; }; - 450E64CB2B570C2700A74F6D /* TranslatedLanguageNameRepository */ = { - isa = PBXGroup; - children = ( - 450E64D02B570C2700A74F6D /* TranslatedLanguageNameRepository.swift */, - 450E64CF2B570C2700A74F6D /* TranslatedLanguageNameDataModel.swift */, - 450E64CC2B570C2700A74F6D /* Cache */, - 45020C542B571BC90029B9CF /* Sync */, - ); - path = TranslatedLanguageNameRepository; - sourceTree = ""; - }; - 450E64CC2B570C2700A74F6D /* Cache */ = { - isa = PBXGroup; - children = ( - 450E64CE2B570C2700A74F6D /* RealmTranslatedLanguageNameCache.swift */, - 450E64CD2B570C2700A74F6D /* RealmTranslatedLanguageName.swift */, - ); - path = Cache; - sourceTree = ""; - }; 450EE27D29A6648900524F64 /* ToolPath */ = { isa = PBXGroup; children = ( @@ -3812,10 +3846,9 @@ 453AC10728F4A10400291791 /* FollowUpsService */, 4504D00228BD00C500D8A5FD /* GodToolsParserLogger */, 4563D99D2A94FC4700E0701C /* InfoPlist */, - 4554512328948D090072EC6E /* LanguageSettingsRepository */, 45D63E5F288F698C009B4610 /* LanguagesRepository */, 4533C4CC28AC2C4C00F9628B /* LaunchCountRepository */, - 45E41B702A83D14600F886A2 /* LocalizationServices */, + 45CC43052C3898B300B0D82B /* LocalizationLanguageNameRepository */, 452E44C22AB8EFD100A0B1B6 /* MobileContentApi */, D409F2AB2928042100D9D2FE /* MobileContentApiAuthSession */, D4B05FF3291061E8005852D0 /* MobileContentAuthTokenRepository */, @@ -3826,7 +3859,6 @@ 45BDA5B92954F5CF007E259B /* ResourceViewsService */, 45F7BDCC2B7CF0A000BC9691 /* ToolDownloader */, 450D7AF928E32965006C3FDF /* TrackDownloadedTranslationsRepository */, - 450E64CB2B570C2700A74F6D /* TranslatedLanguageNameRepository */, 45B36523288504D40012BE53 /* TranslationsRepository */, 45052131295F3B9C0018ABD0 /* UserCountersRepository */, D409F2AF2928115600D9D2FE /* UserDetailsRepository */, @@ -4090,7 +4122,7 @@ 451CEDCF282C31C6006E5105 /* ToolSettings */ = { isa = PBXGroup; children = ( - 45EA5AA82B22474900D9E330 /* Data */, + D4BE3F682C09065300887BFD /* Data */, 45EA5AAA2B22475A00D9E330 /* Data-DomainInterface */, 45EA5AA92B22474900D9E330 /* DependencyContainer */, 45C151EA2A43D1BD00F2A1E8 /* Domain */, @@ -4581,6 +4613,9 @@ 453689FE2ABB2D840028A570 /* Common */ = { isa = PBXGroup; children = ( + 45CC42FB2C38982700B0D82B /* LocaleLanguageName */, + 45CC42FD2C38982800B0D82B /* LocaleLanguageRegionName */, + 45CC42FF2C38982800B0D82B /* LocaleLanguageScriptName */, 453689FF2ABB2D840028A570 /* SharedAppleExtensions */, ); path = Common; @@ -4624,7 +4659,8 @@ children = ( 45368A092ABB2D840028A570 /* DeepLinkingService */, 45CBDA072BA392FF0007DEC8 /* LaunchCountRepository */, - 45368A0B2ABB2D840028A570 /* LocalizationServices */, + 45CC42F82C38981F00B0D82B /* LocalizationLanguageNameRepository */, + 45EB68E02C334D84008A5FF2 /* LocalizationServices */, 45368A062ABB2D840028A570 /* RealmDatabase */, 459ABF5E2B0BB2910034499D /* ResourcesRepository */, ); @@ -4652,25 +4688,6 @@ path = DeepLinkingService; sourceTree = ""; }; - 45368A0B2ABB2D840028A570 /* LocalizationServices */ = { - isa = PBXGroup; - children = ( - 45368A0C2ABB2D840028A570 /* BundleLoader */, - 45368A0E2ABB2D840028A570 /* LocalizableStringsRepositoryTests.swift */, - 45368A0F2ABB2D840028A570 /* LocalizationServicesTests.swift */, - 45CBDA132BA399750007DEC8 /* MockLocalizationServices.swift */, - ); - path = LocalizationServices; - sourceTree = ""; - }; - 45368A0C2ABB2D840028A570 /* BundleLoader */ = { - isa = PBXGroup; - children = ( - 45368A0D2ABB2D840028A570 /* LocalizableStringsBundleLoaderTests.swift */, - ); - path = BundleLoader; - sourceTree = ""; - }; 45368A102ABB2D840028A570 /* Services */ = { isa = PBXGroup; children = ( @@ -4951,6 +4968,7 @@ isa = PBXGroup; children = ( 453E00062B29FB7D00029402 /* LocaleLanguageRegionName.swift */, + 45300B7E2C34282900E6F97D /* LocaleLanguageRegionNameInterface.swift */, ); path = LocaleLanguageRegionName; sourceTree = ""; @@ -5256,23 +5274,6 @@ path = Models; sourceTree = ""; }; - 4554512328948D090072EC6E /* LanguageSettingsRepository */ = { - isa = PBXGroup; - children = ( - 4554512428948D220072EC6E /* LanguageSettingsRepository.swift */, - 4554512628948D7B0072EC6E /* Cache */, - ); - path = LanguageSettingsRepository; - sourceTree = ""; - }; - 4554512628948D7B0072EC6E /* Cache */ = { - isa = PBXGroup; - children = ( - 4554512928948D9A0072EC6E /* LanguageSettingsCache.swift */, - ); - path = Cache; - sourceTree = ""; - }; 455582D0269F2C1000C3FF14 /* Views */ = { isa = PBXGroup; children = ( @@ -7990,6 +7991,7 @@ 45A8BDD32ACC9EE9007A1EBB /* Lessons */, 45AD180425938A4D00A096A0 /* Menu */, 45AD184625938A4E00A096A0 /* Onboarding */, + D4159D512C2CE0240088A203 /* PersistUserToolLanguageSettings */, 4512C8102B3236C000D162F3 /* Shareables */, 45880D6D2BD2B713008F021C /* ShareGodTools */, 45D814CB2B81050900AD00C5 /* SpotlightTools */, @@ -8379,6 +8381,7 @@ 455EE8472AEC5F4A00C3205C /* ProgressTimer */, 45EB9B4429F16CF200CA74A8 /* SharedAppleExtensions */, D4670B0A2AFAA17C00FAD896 /* StringSearcher */, + 45E4DBB52BECFB09006ED2F3 /* SyncInvalidator */, 45D63E54288F67F8009B4610 /* URLSession */, ); path = Common; @@ -8507,6 +8510,7 @@ isa = PBXGroup; children = ( 45AF070E2B066266000EACF2 /* LocaleLanguageName.swift */, + 45300B7C2C34282200E6F97D /* LocaleLanguageNameInterface.swift */, ); path = LocaleLanguageName; sourceTree = ""; @@ -8515,6 +8519,7 @@ isa = PBXGroup; children = ( 45AF07132B0663FD000EACF2 /* LocaleLanguageScriptName.swift */, + 45300B802C34283200E6F97D /* LocaleLanguageScriptNameInterface.swift */, ); path = LocaleLanguageScriptName; sourceTree = ""; @@ -8552,6 +8557,7 @@ 45B2E7182BC044F70077A3C3 /* AppLanguage */ = { isa = PBXGroup; children = ( + 450ADC412BECFE1F00988455 /* Data */, 45B2E7192BC044F70077A3C3 /* Data-DomainInterface */, ); path = AppLanguage; @@ -8669,8 +8675,11 @@ children = ( 45B3F4462AC3A86100D61BFD /* AppLanguagesRepository.swift */, 45B3F4472AC3A86100D61BFD /* AppLanguageDataModel.swift */, + 45E4DBC32BECFB20006ED2F3 /* AppLanguageDataModelInterface.swift */, 45DAC8C52B2BB9E500A138C0 /* AppLanguageDataModel+TranslatableLanguage.swift */, - 45D318F82AE7009700D74A87 /* Cache */, + 45E4DBC02BECFB20006ED2F3 /* Api */, + 45E4DBBC2BECFB20006ED2F3 /* Cache */, + 45E4DBC42BECFB20006ED2F3 /* Sync */, ); path = AppLanguagesRepository; sourceTree = ""; @@ -9109,6 +9118,47 @@ path = Data; sourceTree = ""; }; + 45CC42F82C38981F00B0D82B /* LocalizationLanguageNameRepository */ = { + isa = PBXGroup; + children = ( + 45CC42F72C38981F00B0D82B /* MockLocalizationLanguageNameRepository.swift */, + ); + path = LocalizationLanguageNameRepository; + sourceTree = ""; + }; + 45CC42FB2C38982700B0D82B /* LocaleLanguageName */ = { + isa = PBXGroup; + children = ( + 45CC42FA2C38982700B0D82B /* MockLocaleLanguageName.swift */, + ); + path = LocaleLanguageName; + sourceTree = ""; + }; + 45CC42FD2C38982800B0D82B /* LocaleLanguageRegionName */ = { + isa = PBXGroup; + children = ( + 45CC42FC2C38982800B0D82B /* MockLocaleLanguageRegionName.swift */, + ); + path = LocaleLanguageRegionName; + sourceTree = ""; + }; + 45CC42FF2C38982800B0D82B /* LocaleLanguageScriptName */ = { + isa = PBXGroup; + children = ( + 45CC42FE2C38982800B0D82B /* MockLocaleLanguageScriptName.swift */, + ); + path = LocaleLanguageScriptName; + sourceTree = ""; + }; + 45CC43052C3898B300B0D82B /* LocalizationLanguageNameRepository */ = { + isa = PBXGroup; + children = ( + 45CC43032C3898B300B0D82B /* LocalizationLanguageNameRepository.swift */, + 45CC43042C3898B300B0D82B /* LocalizationLanguageNameRepositoryInterface.swift */, + ); + path = LocalizationLanguageNameRepository; + sourceTree = ""; + }; 45CCF67D2B22667B0017EDFE /* Subviews */ = { isa = PBXGroup; children = ( @@ -9144,14 +9194,6 @@ path = CreatingToolScreenShareSession; sourceTree = ""; }; - 45D318F82AE7009700D74A87 /* Cache */ = { - isa = PBXGroup; - children = ( - 45D318F92AE700A200D74A87 /* AppLanguagesCache.swift */, - ); - path = Cache; - sourceTree = ""; - }; 45D48C8729F81692004E92B1 /* AppConfig */ = { isa = PBXGroup; children = ( @@ -9862,34 +9904,42 @@ path = ContentAnimation; sourceTree = ""; }; - 45E41B702A83D14600F886A2 /* LocalizationServices */ = { + 45E4DBB52BECFB09006ED2F3 /* SyncInvalidator */ = { isa = PBXGroup; children = ( - 45E41B772A83D14600F886A2 /* LocalizationServices.swift */, - 45CBDA112BA3993F0007DEC8 /* LocalizationServicesInterface.swift */, - 45E41B7D2A83D48600F886A2 /* LocalizableStringsRepository.swift */, - 45E41B742A83D14600F886A2 /* Bundle */, - 45E41B712A83D14600F886A2 /* BundleLoader */, + 45E4DBB62BECFB09006ED2F3 /* SyncInvalidator.swift */, + 45E4DBB72BECFB09006ED2F3 /* SyncInvalidatorTimeInterval.swift */, + 45E4DBB82BECFB09006ED2F3 /* SyncInvalidatorManager.swift */, ); - path = LocalizationServices; + path = SyncInvalidator; sourceTree = ""; }; - 45E41B712A83D14600F886A2 /* BundleLoader */ = { + 45E4DBBC2BECFB20006ED2F3 /* Cache */ = { isa = PBXGroup; children = ( - 45E41B732A83D14600F886A2 /* LocalizableStringsBundleLoader.swift */, - 45E41B722A83D14600F886A2 /* LocalizableStringsFileType.swift */, + 45E4DBBF2BECFB20006ED2F3 /* RealmAppLanguagesCache.swift */, + 45E4DBBE2BECFB20006ED2F3 /* RealmAppLanguage.swift */, + 45E4DBBD2BECFB20006ED2F3 /* RealmAppLanguageDirection.swift */, ); - path = BundleLoader; + path = Cache; sourceTree = ""; }; - 45E41B742A83D14600F886A2 /* Bundle */ = { + 45E4DBC02BECFB20006ED2F3 /* Api */ = { isa = PBXGroup; children = ( - 45E41B762A83D14600F886A2 /* LocaleLocalizableStringsBundle.swift */, - 45E41B752A83D14600F886A2 /* LocalizableStringsBundle.swift */, + 45E4DBC12BECFB20006ED2F3 /* AppLanguagesApi.swift */, + 45E4DBC22BECFB20006ED2F3 /* AppLanguageCodable.swift */, ); - path = Bundle; + path = Api; + sourceTree = ""; + }; + 45E4DBC42BECFB20006ED2F3 /* Sync */ = { + isa = PBXGroup; + children = ( + 45E4DBC62BECFB20006ED2F3 /* AppLanguagesRepositorySync.swift */, + 45E4DBC52BECFB20006ED2F3 /* AppLanguagesRepositorySyncInterface.swift */, + ); + path = Sync; sourceTree = ""; }; 45E5851B2A8E5C7F005B20A6 /* ToolFilterButtonView */ = { @@ -9978,6 +10028,7 @@ 4512D4CB2B29127200DFAFB3 /* ToolSettingsToolLanguageDomainModel.swift */, 4512D4CC2B29127200DFAFB3 /* ToolSettingsToolLanguagesListInterfaceStringsDomainModel.swift */, 450B7FC12B2B53A2000B9035 /* ToolSettingsToolLanguagesListTypeDomainModel.swift */, + D4BE3F772C0E967200887BFD /* UserToolSettingsDomainModel.swift */, 45C649BD2B31E178000249E0 /* ViewShareToolDomainModel.swift */, 45EA5AA02B2246A800D9E330 /* ViewToolSettingsDomainModel.swift */, 4512D4CA2B29127200DFAFB3 /* ViewToolSettingsToolLanguagesListDomainModel.swift */, @@ -9985,13 +10036,6 @@ path = Entities; sourceTree = ""; }; - 45EA5AA82B22474900D9E330 /* Data */ = { - isa = PBXGroup; - children = ( - ); - path = Data; - sourceTree = ""; - }; 45EA5AA92B22474900D9E330 /* DependencyContainer */ = { isa = PBXGroup; children = ( @@ -10033,6 +10077,14 @@ path = VideoModal; sourceTree = ""; }; + 45EB68E02C334D84008A5FF2 /* LocalizationServices */ = { + isa = PBXGroup; + children = ( + 45EB68DF2C334D84008A5FF2 /* MockLocalizationServices.swift */, + ); + path = LocalizationServices; + sourceTree = ""; + }; 45EB9B4429F16CF200CA74A8 /* SharedAppleExtensions */ = { isa = PBXGroup; children = ( @@ -10778,7 +10830,6 @@ 457C8585242E50D70025E079 /* GoogleService-Info-Debug.plist */, 1737DFDE22FDB48000223CB0 /* GoogleService-Info.plist */, 4F57329B1EA69CF00082035C /* Info.plist */, - 4F5732981EA69CF00082035C /* LaunchScreen.storyboard */, D4B522DB29D761B700D85213 /* Localizable.stringsdict */, 4FAF207A1F02933900C43AFC /* Localizable.strings */, D471CFBF28B3D67100DCE61C /* Preview Content */, @@ -10932,6 +10983,57 @@ path = Models; sourceTree = ""; }; + D4159D512C2CE0240088A203 /* PersistUserToolLanguageSettings */ = { + isa = PBXGroup; + children = ( + D4159D572C2CE1D50088A203 /* Data */, + D4159D562C2CE18C0088A203 /* Data-DomainInterface */, + D4159D522C2CE0B80088A203 /* Domain */, + ); + path = PersistUserToolLanguageSettings; + sourceTree = ""; + }; + D4159D522C2CE0B80088A203 /* Domain */ = { + isa = PBXGroup; + children = ( + D4159D552C2CE1570088A203 /* Interfaces */, + D4159D532C2CE0BE0088A203 /* UseCases */, + ); + path = Domain; + sourceTree = ""; + }; + D4159D532C2CE0BE0088A203 /* UseCases */ = { + isa = PBXGroup; + children = ( + D4BE3F622C08074400887BFD /* PersistUserToolLanguageSettingsUseCase.swift */, + ); + path = UseCases; + sourceTree = ""; + }; + D4159D552C2CE1570088A203 /* Interfaces */ = { + isa = PBXGroup; + children = ( + D4BE3F642C09029900887BFD /* PersistUserToolLanguageSettingsRepositoryInterface.swift */, + ); + path = Interfaces; + sourceTree = ""; + }; + D4159D562C2CE18C0088A203 /* Data-DomainInterface */ = { + isa = PBXGroup; + children = ( + D4BE3F662C09048C00887BFD /* PersistUserToolLanguageSettingsRepository.swift */, + ); + path = "Data-DomainInterface"; + sourceTree = ""; + }; + D4159D572C2CE1D50088A203 /* Data */ = { + isa = PBXGroup; + children = ( + D4BE3F6B2C0907AE00887BFD /* UserToolSettingsRepository */, + ); + path = Data; + sourceTree = ""; + }; D42FEBDD287605D60002FAD9 /* OptInOnboardingBannerEnabledRepository */ = { isa = PBXGroup; children = ( @@ -11238,6 +11340,32 @@ path = SetCompletedTrainingTipUseCase; sourceTree = ""; }; + D4BE3F682C09065300887BFD /* Data */ = { + isa = PBXGroup; + children = ( + ); + path = Data; + sourceTree = ""; + }; + D4BE3F6B2C0907AE00887BFD /* UserToolSettingsRepository */ = { + isa = PBXGroup; + children = ( + D4BE3F712C0909BA00887BFD /* UserToolSettingsDataModel.swift */, + D4EAF2F82C0F95D900AEC36C /* UserToolSettingsRepository.swift */, + D4BE3F6C2C0907B700887BFD /* Cache */, + ); + path = UserToolSettingsRepository; + sourceTree = ""; + }; + D4BE3F6C2C0907B700887BFD /* Cache */ = { + isa = PBXGroup; + children = ( + D4BE3F6D2C0907E900887BFD /* RealmUserToolSettingsCache.swift */, + D4BE3F6F2C0907FC00887BFD /* RealmUserToolSettings.swift */, + ); + path = Cache; + sourceTree = ""; + }; D4EB51D82833E079009C185F /* SwiftUI Modifiers */ = { isa = PBXGroup; children = ( @@ -11293,6 +11421,7 @@ 4F12296820852BE3008842CC /* Sources */, 4F12296920852BE3008842CC /* Frameworks */, 4F12296A20852BE3008842CC /* Resources */, + 452556462C383C3F00AA0046 /* Embed Frameworks */, ); buildRules = ( ); @@ -11312,6 +11441,7 @@ 45A416172A9943D50030E2C7 /* SocialAuthentication */, 45A416192A9943D50030E2C7 /* Starscream */, 45A4161B2A9943D50030E2C7 /* Fuzi */, + 452556432C383C3F00AA0046 /* RealmSwift */, ); productName = godtoolsUITests; productReference = 4F12296C20852BE3008842CC /* godtoolsUITests.xctest */; @@ -11348,6 +11478,7 @@ 45D40C6A2A5315D500E8E4AE /* Fuzi */, 45B4FDBC2BE1787000E5E35B /* AppsFlyerLib-Strict */, 4581EAA22BFBE194008115FF /* RealmSwift */, + 45F671902C3344010017C8C2 /* LocalizationServices */, ); productName = godtools; productReference = 4F5732891EA69CF00082035C /* godtools.app */; @@ -11373,6 +11504,7 @@ 45F486EE2B76D501004E0E27 /* Quick */, 45F486F12B76D513004E0E27 /* Nimble */, 4581EAA42BFBE257008115FF /* RealmSwift */, + 45F671922C33440E0017C8C2 /* LocalizationServices */, ); productName = godtoolsTests; productReference = 684328F11EB7CF66005E9131 /* godtoolsTests.xctest */; @@ -11421,11 +11553,9 @@ }; buildConfigurationList = 4F5732841EA69CF00082035C /* Build configuration list for PBXProject "godtools" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, - Base, fr, ru, ar, @@ -11494,6 +11624,8 @@ fi, ps, ckb, + en, + Base, ); mainGroup = 4F5732801EA69CF00082035C; packageReferences = ( @@ -11509,6 +11641,7 @@ 45F486F02B76D512004E0E27 /* XCRemoteSwiftPackageReference "Nimble" */, 45B4FDBB2BE1787000E5E35B /* XCRemoteSwiftPackageReference "AppsFlyerFramework-Strict" */, 4581EAA12BFBE194008115FF /* XCRemoteSwiftPackageReference "realm-swift" */, + 45F6718F2C3343ED0017C8C2 /* XCRemoteSwiftPackageReference "localization-services-ios" */, ); productRefGroup = 4F57328A1EA69CF00082035C /* Products */; projectDirPath = ""; @@ -11545,7 +11678,6 @@ 45F3D39829A50B1300446F69 /* onboarding_prepare_for_moments.json in Resources */, 4FAF20781F02933900C43AFC /* Localizable.strings in Resources */, 4561C41127C6891600C4A367 /* MobileContentAccordionSectionView.xib in Resources */, - 4F57329A1EA69CF00082035C /* LaunchScreen.storyboard in Resources */, 45F3AF0A2B5ED28E00CE8D41 /* PageNavigationCollectionView.xib in Resources */, 45F3D39A29A50B1300446F69 /* onboarding_help_someone_discover_jesus.json in Resources */, 458CFE8929D4E0B9007B423C /* ArticleCell.xib in Resources */, @@ -11749,7 +11881,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 45D318FD2AE7031D00D74A87 /* AppLanguagesCache.swift in Sources */, 459B7E692ABA3A4A0088F44B /* LaunchEnvironmentKey.swift in Sources */, 45A415F42A99429C0030E2C7 /* XCUIApplication+Query.swift in Sources */, 45FBE5722AB8A33200BFBE92 /* AccessibilityStrings.swift in Sources */, @@ -11763,6 +11894,7 @@ 45DC19122BB5AB2C007542A5 /* LanguageSettingsFlowTests.swift in Sources */, 45DC19172BB5B34D007542A5 /* BaseFlowTests.swift in Sources */, 459B7E6A2ABA3A510088F44B /* AccessibilityStrings.swift in Sources */, + 452556472C383CFA00AA0046 /* AppLanguageDataModelInterface.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -11877,7 +12009,6 @@ 4566DCAE2A2686C000B989A2 /* RealmMobileContentAuthTokenCache.swift in Sources */, 45052146295F3B9D0018ABD0 /* UserCounterDecodable.swift in Sources */, 4521B2822B32853E00C8A473 /* GetShareableImageRepository.swift in Sources */, - 450E64D32B570C2700A74F6D /* TranslatedLanguageNameDataModel.swift in Sources */, 45A835182AD1A414004F5593 /* ToolDetailsMediaDomainModel.swift in Sources */, 45B3652B2885055E0012BE53 /* RealmTranslationsCache.swift in Sources */, 45B6FF3E2BAA39840037B240 /* GetToolFilterCategoriesRepository.swift in Sources */, @@ -11913,6 +12044,7 @@ 4512D4D32B29127C00DFAFB3 /* GetToolSettingsToolLanguagesListInterfaceStringsRepository.swift in Sources */, 4534F93B2AE9B11600A7A071 /* RealmLessonEvaluation.swift in Sources */, 45EC429B272C87430052F2AA /* PassthroughValue.swift in Sources */, + D4EAF2F92C0F95D900AEC36C /* UserToolSettingsRepository.swift in Sources */, 459E86EA28EDA86C00E197A5 /* IncomingDeepLinkType.swift in Sources */, 45F7B0B52AF18E5E00A0E7B4 /* MenuInterfaceStringsDomainModel.swift in Sources */, 45D5EA9A2B7E5B7900745F0D /* ViewConfirmRemoveToolFromFavoritesDomainModel.swift in Sources */, @@ -11962,7 +12094,6 @@ D4F045812BBB4CCC00B115C9 /* UserToolCategoryFilterDataModel.swift in Sources */, 453AC11628F4A10400291791 /* FollowUpModel.swift in Sources */, 455583E7269F2DA500C3FF14 /* MobileContentButtonView.swift in Sources */, - 4554512528948D220072EC6E /* LanguageSettingsRepository.swift in Sources */, 45860A562923E7B30089C836 /* FixedHorizontalSpacer.swift in Sources */, 4564E2062B1E181000DA4040 /* GetToolShortcutLinksRepositoryInterface.swift in Sources */, 457766E92AD6F3340093B19A /* GetToolDetailsInterfaceStringsRepositoryInterface.swift in Sources */, @@ -11974,6 +12105,7 @@ 45AAC2932BB30729000AE690 /* GetGlobalActivityThisWeekRepositoryInterface.swift in Sources */, 45AC9DD82B28A97300DEEBFE /* GetDownloadableLanguagesInterfaceStringsRepository.swift in Sources */, 4512C8212B3236C000D162F3 /* GetShareablesUseCase.swift in Sources */, + 45E4DBCA2BECFB20006ED2F3 /* AppLanguagesApi.swift in Sources */, D1DD79FF26690CBA000F1351 /* TrackActionModel.swift in Sources */, 45828BD3279CCBD200F6B5F3 /* MobileContentFlowViewModel.swift in Sources */, 4598BD2A2BF7DF6800196463 /* SocialSignInButtonView.swift in Sources */, @@ -12052,6 +12184,7 @@ 4564E20E2B1E181000DA4040 /* ToolShortcutLinksDiContainer.swift in Sources */, 4598BD2D2BF7DF6800196463 /* SocialSignInViewModel.swift in Sources */, D45922D62867998200904B87 /* BannerTextStyle.swift in Sources */, + D4BE3F652C09029900887BFD /* PersistUserToolLanguageSettingsRepositoryInterface.swift in Sources */, 45052142295F3B9D0018ABD0 /* RealmUserCountersSync.swift in Sources */, 45558330269F2C7B00C3FF14 /* ToolPageCardsViewModel.swift in Sources */, 4512C8272B32388000D162F3 /* GetShareableImageUseCase.swift in Sources */, @@ -12106,6 +12239,7 @@ D47DF0442B28F9560079219B /* RealmDownloadedLanguagesCache.swift in Sources */, 45C1522F2A43D1BD00F2A1E8 /* ToolSettingsHostingView.swift in Sources */, 454BFDA82AFD8CD200104BC8 /* TractRemoteSharePublisherNavigationEvent.swift in Sources */, + 45CC43062C3898B300B0D82B /* LocalizationLanguageNameRepository.swift in Sources */, 459C526F289418EB00DA9E95 /* TranslationManifestFileDataModel.swift in Sources */, 45BE4A7B2AF28776004021DA /* NSAttributedString+Format.swift in Sources */, 45E60CFF2AEAE1C300E14BEA /* ScaleValueSliderView.swift in Sources */, @@ -12123,11 +12257,11 @@ 4592156A2B82955300387AC5 /* GetSpotlightToolsRepository.swift in Sources */, 45ACBE062B1F64230017D17E /* LessonViewModel.swift in Sources */, 45AD205125938AC300A096A0 /* ToolOpenedAnalytics.swift in Sources */, + 45300B7F2C34282900E6F97D /* LocaleLanguageRegionNameInterface.swift in Sources */, 45C2AF4A2A81764D004958AB /* GTPageControlAttributes.swift in Sources */, 45AE974B27C97A9500C2CB33 /* Animation+MobileContentRenderableModel.swift in Sources */, 45AE974227C97A9400C2CB33 /* FlowItem+MobileContentRenderableModel.swift in Sources */, 459BF7802AFEE0320053BA09 /* RealmToolScreenTutorialShareView.swift in Sources */, - 45E41B782A83D14600F886A2 /* LocalizableStringsFileType.swift in Sources */, 45043FA12BFB875A00324573 /* GetDeleteAccountProgressInterfaceStringsInterface.swift in Sources */, 45645C1D2AFE7A5600BD233D /* ShareToolScreenShareSessionDomainModel.swift in Sources */, 458721252BFB7756005DA242 /* DeleteAccountProgressView.swift in Sources */, @@ -12141,6 +12275,7 @@ 4598BD0E2BF7DF6800196463 /* FacebookAuthentication+AuthenticationProviderInterface.swift in Sources */, 45131A0D2AB498AD0085AF0D /* ColorPalette.swift in Sources */, 456AC1912B8CCA6100169C11 /* TrackShareShareableTap.swift in Sources */, + 45E4DBCD2BECFB20006ED2F3 /* AppLanguagesRepositorySyncInterface.swift in Sources */, 450D7B0128E32965006C3FDF /* RealmDownloadedTranslation.swift in Sources */, 45F71678290A13530019B715 /* RealmEmailSignUp.swift in Sources */, 4598BD152BF7DF6800196463 /* UserAuthentication.swift in Sources */, @@ -12185,6 +12320,7 @@ 45F378FB2B23642600EEF039 /* GetLearnToShareToolInterfaceStringsRepository.swift in Sources */, 4556CEAE29F2E07900643BBC /* ArticleDebugView.swift in Sources */, 454CA2002B7BD30B009A3976 /* ViewAppLanguagesDomainModel.swift in Sources */, + 45E4DBBB2BECFB09006ED2F3 /* SyncInvalidatorManager.swift in Sources */, D496B90E2A9FD39500CBEA19 /* ToolFilterSelection.swift in Sources */, D40D77BA2AAF9891008D3642 /* ToolsFilterSectionView.swift in Sources */, 453F84C22A029FC00005101E /* ArticleAemCacheObject.swift in Sources */, @@ -12242,6 +12378,7 @@ 4597101629CDE02400C47040 /* View+CornerRadius.swift in Sources */, 454CA1FE2B7BC540009A3976 /* GetAppLanguageRepository.swift in Sources */, 4555840D269F2DA500C3FF14 /* MobileContentTextViewModel.swift in Sources */, + 45E4DBB92BECFB09006ED2F3 /* SyncInvalidator.swift in Sources */, 45EB9B7629F16CF200CA74A8 /* UIView+Constraints.swift in Sources */, 455EE8442AEC59EB00C3205C /* DownloadToolProgressFeatureDataLayerDependencies.swift in Sources */, 4598BD2B2BF7DF6800196463 /* SocialSignInAuthenticationType.swift in Sources */, @@ -12266,6 +12403,7 @@ 450D63B22AC8A87200B90319 /* TrackScreenViewAnalyticsInterface.swift in Sources */, 459E86EB28EDA86C00E197A5 /* DeepLinkingParserManifestType.swift in Sources */, 453F84BC2A029FC00005101E /* ArticleAemWebArchiveFileCacheLocation.swift in Sources */, + 45CC43072C3898B300B0D82B /* LocalizationLanguageNameRepositoryInterface.swift in Sources */, D4E243D029EDD2E800EA4BB7 /* AskAQuestionWebContent.swift in Sources */, 45E3476D2A49BE230014CCD1 /* Flow+NavigateToUrl.swift in Sources */, 457E199A27C6E3AF00BABD49 /* MobileContentRenderer.swift in Sources */, @@ -12397,6 +12535,7 @@ 45ACBE092B1F64230017D17E /* LessonView.swift in Sources */, 45EA98FE2AFD691200E7EA9A /* GetCreatingToolScreenShareSessionTimedOutInterfaceStringsRepository.swift in Sources */, 45828BC8279CCB5300F6B5F3 /* MobileContentCardView.swift in Sources */, + 45E4DBC82BECFB20006ED2F3 /* RealmAppLanguage.swift in Sources */, 45D63EA6288F77D4009B4610 /* MobileContentResourcesApi.swift in Sources */, D4670B072AFA9EE900FAD896 /* StringSearchable.swift in Sources */, 45558341269F2C7B00C3FF14 /* ToolPageHeaderView.swift in Sources */, @@ -12434,12 +12573,14 @@ 45DAC8C62B2BB9E500A138C0 /* AppLanguageDataModel+TranslatableLanguage.swift in Sources */, 45F71625290A12D70019B715 /* CopyrightInfoWebContent.swift in Sources */, 45A8BDE22ACC9F47007A1EBB /* LessonsView.swift in Sources */, + D4BE3F6E2C0907E900887BFD /* RealmUserToolSettingsCache.swift in Sources */, 456714142B7E9FDC00C6E7A2 /* GetTranslatedToolName.swift in Sources */, 45AD1BD325938A4F00A096A0 /* LoadingView.swift in Sources */, 45A8351A2AD1A578004F5593 /* ToolDetailsScrollViewTopView.swift in Sources */, 450DB5602AC354C000B2FE3A /* GetCurrentAppLanguageUseCase.swift in Sources */, 45369AD82AFA7FA500BD10F0 /* ToolScreenShareTutorialViewModel.swift in Sources */, 4578783027C41C08004552D5 /* MobileContentBackgroundImageAlignment.swift in Sources */, + D4BE3F722C0909BA00887BFD /* UserToolSettingsDataModel.swift in Sources */, 454ADAD72ABDD9D400037C27 /* LanguageSettingsAppInterfaceLanguageView.swift in Sources */, 45D63E70288F698C009B4610 /* LanguagesRepository.swift in Sources */, 4556CEB029F2E09500643BBC /* ArticleDebugViewModel.swift in Sources */, @@ -12492,6 +12633,7 @@ 457579E426E96ABE00C898D2 /* MobileContentMultiSelectView.swift in Sources */, 45931FF92B34C1E3008034C8 /* ViewReviewShareShareableDomainModel.swift in Sources */, 45F71630290A12D70019B715 /* MailViewModelType.swift in Sources */, + 45E4DBCC2BECFB20006ED2F3 /* AppLanguageDataModelInterface.swift in Sources */, 45AAC29A2BB307FE000AE690 /* GlobalActivityDiContainer.swift in Sources */, 452486CE2B07C987007AD932 /* SearchToolFilterLanguagesUseCase.swift in Sources */, 45131A112AB498AD0085AF0D /* AccessibilityStrings.swift in Sources */, @@ -12512,6 +12654,7 @@ 45B3F4522AC3A92700D61BFD /* AppLanguageFeatureDomainLayerDependencies.swift in Sources */, 452DFB342B7F9FE2003A4A59 /* ToolDomainModel.swift in Sources */, 45A835172AD1A414004F5593 /* ToolVersionDomainModel.swift in Sources */, + D4BE3F672C09048C00887BFD /* PersistUserToolLanguageSettingsRepository.swift in Sources */, 45308EBD2BDC254400A49D96 /* GetSpiritualConversationReadinessScale.swift in Sources */, 45FE82392ACE5BFE00930C39 /* AppHostingController.swift in Sources */, 451CEDFD282C3239006E5105 /* ToolSettingsFlow.swift in Sources */, @@ -12561,9 +12704,7 @@ 452486DB2B07C991007AD932 /* LanguageFilterDomainModel.swift in Sources */, 458E410B2A9CCE55009769DF /* AppBuildConfiguration.swift in Sources */, 45677780287DB21F0044AEB2 /* ToolTrainingTipView.swift in Sources */, - 450E64D12B570C2700A74F6D /* RealmTranslatedLanguageName.swift in Sources */, 4512D4CE2B29127200DFAFB3 /* ToolSettingsToolLanguageDomainModel.swift in Sources */, - 45D318FA2AE700A200D74A87 /* AppLanguagesCache.swift in Sources */, 458B91B32B7D5D6800785C6F /* AllYourFavoriteToolsViewModel.swift in Sources */, 4595E0E32A852DAA00D88DAE /* MobileContentApiUsersMeCodable+UserDetailsDataModelType.swift in Sources */, 45AD1F6325938A9800A096A0 /* WebSocketType.swift in Sources */, @@ -12580,7 +12721,6 @@ 45D5EA9E2B7E5C2300745F0D /* GetConfirmRemoveToolFromFavoritesInterfaceStringsRepositoryInterface.swift in Sources */, 45AE975F27C97A9500C2CB33 /* Text+MobileContentRenderableModel.swift in Sources */, 45E8FAA72882FD4B00D7D569 /* RealmSHA256File.swift in Sources */, - 45E41B792A83D14600F886A2 /* LocalizableStringsBundleLoader.swift in Sources */, 455583DB269F2DA500C3FF14 /* MobileContentAccordionViewModel.swift in Sources */, 4555833B269F2C7B00C3FF14 /* ToolPageHeroViewModel.swift in Sources */, 4587212A2BFB7762005DA242 /* DeleteAccountUseCase.swift in Sources */, @@ -12601,6 +12741,7 @@ 458CFE8A29D4E0B9007B423C /* ArticlesViewModel.swift in Sources */, 4552F54A2AD4745A0075415E /* TrackScreenViewAnalyticsUseCase.swift in Sources */, 457032A526D0055A00F5BADC /* MobileContentBackgroundImageRenderer.swift in Sources */, + D4BE3F702C0907FC00887BFD /* RealmUserToolSettings.swift in Sources */, 450EE2AB29A6719500524F64 /* GodToolsAppLessonsPathDeepLinkParser.swift in Sources */, 454BFDA72AFD8CD200104BC8 /* TractRemoteShareNavigationEvent.swift in Sources */, 45EA98FA2AFD67FC00E7EA9A /* ViewCreatingToolScreenShareSessionTimedOutUseCase.swift in Sources */, @@ -12621,6 +12762,7 @@ 458721322BFB7E9D005DA242 /* DeleteAccountInterfaceStringsDomainModel.swift in Sources */, 45F7E7602701F1AA00CC9FB9 /* MobileContentViewVisibilityState.swift in Sources */, 458CFEA729D4E172007B423C /* GetUserActivityStatsUseCase.swift in Sources */, + 45E4DBC92BECFB20006ED2F3 /* RealmAppLanguagesCache.swift in Sources */, 459BD090289AD4310075901B /* GetToolTranslationsFilesUseCase.swift in Sources */, 4512D4C42B29124600DFAFB3 /* GetToolSettingsToolLanguagesListRepositoryInterface.swift in Sources */, 4564E20B2B1E181000DA4040 /* ToolShortcutLinksViewModel.swift in Sources */, @@ -12644,7 +12786,6 @@ 453F84DA2A02A0390005101E /* ArticleManifestAemRepository.swift in Sources */, 45D63E6E288F698C009B4610 /* RealmLanguage.swift in Sources */, 45EF23AC2B37314400E49716 /* JsonApiResponseBaseData.swift in Sources */, - 45E41B7C2A83D14600F886A2 /* LocalizationServices.swift in Sources */, 45043F9F2BFB871400324573 /* ViewDeleteAccountProgressDomainModel.swift in Sources */, 45AD20B225938EE500A096A0 /* FileCache.swift in Sources */, 4556CEA829F2D57800643BBC /* RealmDatabaseProductionConfiguration.swift in Sources */, @@ -12703,6 +12844,7 @@ 45DE9ACE287865DB004E3AE0 /* OptInOnboardingBannerEnabledCache.swift in Sources */, 455583FF269F2DA500C3FF14 /* MobileContentEmbeddedVideoViewModel.swift in Sources */, 455583F3269F2DA500C3FF14 /* MobileContentTabViewModel.swift in Sources */, + 45E4DBCE2BECFB20006ED2F3 /* AppLanguagesRepositorySync.swift in Sources */, 4550BC702A33AC6D00256DEE /* HashableCurrentValueSubject.swift in Sources */, 452DFB322B7F9F42003A4A59 /* ToolListItemInterfaceStringsDomainModel.swift in Sources */, 45308EC12BDC2A4500A49D96 /* ScaleValue.swift in Sources */, @@ -12741,6 +12883,7 @@ 45AC9DEC2B28A9CD00DEEBFE /* DownloadableLanguageItemView.swift in Sources */, 45AE974C27C97A9500C2CB33 /* Tabs+MobileContentRenderableModel.swift in Sources */, 45AE974427C97A9400C2CB33 /* Modal+MobileContentRenderableModel.swift in Sources */, + D4BE3F782C0E967200887BFD /* UserToolSettingsDomainModel.swift in Sources */, D4AFA44F2BAD0A9200318023 /* GetToolFilterCategoriesInterfaceStringsRepository.swift in Sources */, 4529B96E289345480011F16B /* TranslationManifestParserType.swift in Sources */, 4555840C269F2DA500C3FF14 /* MobileContentErrorViewModel.swift in Sources */, @@ -12766,6 +12909,7 @@ 45C2AF3A2A813371004958AB /* PageControl.swift in Sources */, 45E3478A2A49C0120014CCD1 /* OptionalImage.swift in Sources */, D409F2BA292C017C00D9D2FE /* RealmUserDetails.swift in Sources */, + 45300B7D2C34282200E6F97D /* LocaleLanguageNameInterface.swift in Sources */, 453F84C72A029FC00005101E /* ArticleAemJcrContentParser.swift in Sources */, D455F3F62979CE5D009D5F93 /* RemoteUserCountersSync.swift in Sources */, 455583EC269F2DA500C3FF14 /* MobileContentSpacerView.swift in Sources */, @@ -12792,8 +12936,10 @@ 45AC77D829DCA3DA00A2224B /* MobileContentPageRendererPagesViewDataCache.swift in Sources */, 452DFB3D2B7FB456003A4A59 /* GetAllYourFavoritedToolsInterfaceStringsRepositoryInterface.swift in Sources */, 4585E4B52B05739100223A9B /* BCP47LanguageIdentifier.swift in Sources */, + D4BE3F632C08074400887BFD /* PersistUserToolLanguageSettingsUseCase.swift in Sources */, 45C152382A43D1BD00F2A1E8 /* ToolSettingsShareablesView.swift in Sources */, 452486D22B07C98B007AD932 /* GetUserToolFiltersRepositoryInterface.swift in Sources */, + 45300B812C34283200E6F97D /* LocaleLanguageScriptNameInterface.swift in Sources */, 45D63EA2288F77D4009B4610 /* ResourceModelType.swift in Sources */, D48018C028EF650700389565 /* DashboardView.swift in Sources */, 4598BD202BF7DF6800196463 /* AuthUserDomainModel.swift in Sources */, @@ -12821,7 +12967,6 @@ 4534F9242AE99EF700A7A071 /* LessonEvaluationFeatureDataLayerDependencies.swift in Sources */, 45A8BE092ACC9FD5007A1EBB /* LessonsFeatureDiContainer.swift in Sources */, 45AF07142B0663FD000EACF2 /* LocaleLanguageScriptName.swift in Sources */, - 45E41B7B2A83D14600F886A2 /* LocaleLocalizableStringsBundle.swift in Sources */, 4534F9402AE9B65900A7A071 /* CancelLessonEvaluationUseCase.swift in Sources */, 45AE974527C97A9400C2CB33 /* CallToAction+MobileContentRenderableModel.swift in Sources */, 458CFE9529D4E0B9007B423C /* ArticleCategoriesView.swift in Sources */, @@ -12845,7 +12990,6 @@ 456423E72C00B72400B0B67E /* StoreInitialFavoritedToolsInterface.swift in Sources */, 45C8835F2A93EE9C00F33E6D /* DashboardTabBarItemViewModel.swift in Sources */, 45AE974327C97A9400C2CB33 /* TractPage+MobileContentRenderableModel.swift in Sources */, - 45E41B7A2A83D14600F886A2 /* LocalizableStringsBundle.swift in Sources */, 45325F24285CF8B40078D932 /* Segment.swift in Sources */, 458CFE9029D4E0B9007B423C /* ArticleWebViewModel.swift in Sources */, 45373D782BE112700058E990 /* LocalizableStringDictKeys.swift in Sources */, @@ -12855,10 +12999,8 @@ 4573133B28C1821100481640 /* ToolDetailsTitleHeaderView.swift in Sources */, 45B6FF312BAA39720037B240 /* GetToolFilterCategoriesRepositoryInterface.swift in Sources */, 45558320269F2C7B00C3FF14 /* ToolPageView.swift in Sources */, - 450E64D22B570C2700A74F6D /* RealmTranslatedLanguageNameCache.swift in Sources */, 45131A142AB4995F0085AF0D /* ApplicationLayout.swift in Sources */, 45AE974127C97A9400C2CB33 /* Video+MobileContentRenderableModel.swift in Sources */, - 450E64D42B570C2700A74F6D /* TranslatedLanguageNameRepository.swift in Sources */, 45F267A128907F5D006679F2 /* RealmResourcesCacheSync.swift in Sources */, 45558352269F2D1000C3FF14 /* LessonPageView.swift in Sources */, 45FB1CC2295F4184002BACD9 /* LayoutDirection+LanguageDirectionDomainModel.swift in Sources */, @@ -12887,6 +13029,7 @@ 45EA5AB02B22482200D9E330 /* ToolSettingsDataLayerDependencies.swift in Sources */, 4512C80A2B322D5900D162F3 /* ToolSettingsShareableItemViewModel.swift in Sources */, 45039E102B1F5ADD009E1D96 /* ViewToolDetailsUseCase.swift in Sources */, + 45E4DBC72BECFB20006ED2F3 /* RealmAppLanguageDirection.swift in Sources */, 4561AC9A279C4CC6003718C0 /* MobileContentCardCollectionPageView.swift in Sources */, 45EA5AAC2B22476300D9E330 /* GetToolSettingsInterfaceStringsRepository.swift in Sources */, 45D814D92B8105AB00AD00C5 /* SpotlightToolListItemDomainModel.swift in Sources */, @@ -12924,7 +13067,6 @@ 45AF070F2B066266000EACF2 /* LocaleLanguageName.swift in Sources */, 45131A0F2AB498AD0085AF0D /* FontLibrary.swift in Sources */, D4B05FF929106462005852D0 /* MobileContentAuthTokenAPI.swift in Sources */, - 45020C562B571BC90029B9CF /* TranslatedLanguageNameRepositorySync.swift in Sources */, 4504BA1E2AF3ED4C001151E5 /* TutorialItemView.swift in Sources */, 45B3382B2AF42A3900D18C63 /* GetTutorialInterfaceStringsRepositoryInterface.swift in Sources */, 45AAC2992BB307FE000AE690 /* GlobalActivityDataLayerDependencies.swift in Sources */, @@ -12945,7 +13087,6 @@ 459BD08F289AD4310075901B /* ToolTranslationsDomainModel.swift in Sources */, 458CFE9629D4E0B9007B423C /* ArticleCategoriesViewModel.swift in Sources */, 450069932B194744008A7499 /* ToolLanguageAvailableOfflineLanguageView.swift in Sources */, - 4554512A28948D9A0072EC6E /* LanguageSettingsCache.swift in Sources */, 453AC11528F4A10400291791 /* FollowUpsApi.swift in Sources */, 45E066D42AE16BDD004393CD /* StoreOnboardingTutorialViewedRepository.swift in Sources */, 45052143295F3B9D0018ABD0 /* UserCounterDataModel.swift in Sources */, @@ -12955,7 +13096,6 @@ 455583D5269F2DA500C3FF14 /* MobileContentHeaderViewModel.swift in Sources */, 45D63E5A288F6896009B4610 /* TranslationModelType.swift in Sources */, 4555832F269F2C7B00C3FF14 /* ToolPageCardTopConstantState.swift in Sources */, - 45CBDA122BA3993F0007DEC8 /* LocalizationServicesInterface.swift in Sources */, 45ACEE1C2AD0493F004B62E4 /* SearchBarView.swift in Sources */, 4585BF5E2AC38D1B00A6D720 /* LanguageCodeDomainModel.swift in Sources */, 457C36E82A96607F001288D8 /* PageNavigationCollectionViewLayoutType.swift in Sources */, @@ -13044,6 +13184,7 @@ 4535BE142B348BF100A8B62F /* ShareShareableViewModel.swift in Sources */, 45B6FF3D2BAA39840037B240 /* SearchToolFilterCategoriesRepository.swift in Sources */, 45F3D39229A4FC9A00446F69 /* OnboardingTutorialReadyForEveryConversationViewModel.swift in Sources */, + 45E4DBBA2BECFB09006ED2F3 /* SyncInvalidatorTimeInterval.swift in Sources */, 450D63AC2AC8A84600B90319 /* TrackActionAnalyticsPropertiesDomainModel.swift in Sources */, 4598BD112BF7DF6800196463 /* AuthenticationProviderResponse.swift in Sources */, 45308EBF2BDC27C800A49D96 /* LessonEvaluationScaleDomainModel.swift in Sources */, @@ -13113,7 +13254,6 @@ 453A4F382BED6B02006E8B01 /* MobileContentAccordionSectionPositionState.swift in Sources */, 450069902B194371008A7499 /* ToolLanguagesAvailableOfflineView.swift in Sources */, 45A8E4352AFC0ABD008EF03D /* ViewCreatingToolScreenShareSessionUseCase.swift in Sources */, - 45E41B7E2A83D48600F886A2 /* LocalizableStringsRepository.swift in Sources */, 45D08D782AFACBB700ADA673 /* CreatingToolScreenShareSessionViewModel.swift in Sources */, 453A2EF72B3636DA00C8F865 /* DownloadToolLanguageUseCase.swift in Sources */, 45558337269F2C7B00C3FF14 /* ToolPageCallToActionView.swift in Sources */, @@ -13156,6 +13296,7 @@ D43FC1D62B814F2000F8310E /* GetAccountInterfaceStringsRepository.swift in Sources */, 45FB160827DBDDB60009DF8E /* LessonFlow.swift in Sources */, 45B6FF372BAA39790037B240 /* ViewToolFilterLanguagesDomainModel.swift in Sources */, + 45E4DBCB2BECFB20006ED2F3 /* AppLanguageCodable.swift in Sources */, 4534F9202AE99EBC00A7A071 /* GetLessonEvaluationInterfaceStringsRepository.swift in Sources */, 45D63E76288F698C009B4610 /* MobileContentLanguagesApi.swift in Sources */, 454B47E82AE024E000C7B5E7 /* OnboardingDataLayerDependencies.swift in Sources */, @@ -13182,29 +13323,31 @@ 45E198692BA4822A00BF14F3 /* TestsDiContainer.swift in Sources */, 450FB88A2BFBA0F80015D945 /* StoreInitialAppLanguageTests.swift in Sources */, 450FB88B2BFBA0F80015D945 /* GetLanguageSettingsInterfaceStringsRepositoryTests.swift in Sources */, + 45CC42F92C38981F00B0D82B /* MockLocalizationLanguageNameRepository.swift in Sources */, 450FB88C2BFBA0F80015D945 /* SearchAppLanguageInAppLanguagesListRepositoryTests.swift in Sources */, 450FB88D2BFBA0F80015D945 /* GetAppLanguagesInterfaceStringsRepositoryTests.swift in Sources */, 450FB88E2BFBA0F80015D945 /* SearchLanguageInDownloadableLanguagesRepositoryTests.swift in Sources */, - 45368A1A2ABB2D850028A570 /* LocalizableStringsBundleLoaderTests.swift in Sources */, + 45CC43022C38982800B0D82B /* MockLocaleLanguageScriptName.swift in Sources */, 45A45CEE2731B8C9005D4E74 /* MobileContentBackgroundImageRendererType.swift in Sources */, + 45CC43002C38982800B0D82B /* MockLocaleLanguageName.swift in Sources */, 450FB8892BFBA0EB0015D945 /* SearchToolFilterCategoriesRepositoryTests.swift in Sources */, 45AFC62D27CD74100090E381 /* MobileContentBackgroundImageAlignment.swift in Sources */, 45E1986B2BA488C800BF14F3 /* TestsInMemoryRealmDatabase.swift in Sources */, 450FB8902BFBA1060015D945 /* RealmDatabaseDeleteTests.swift in Sources */, 450FB8912BFBA1060015D945 /* RealmDatabaseReadTests.swift in Sources */, 450FB8922BFBA1060015D945 /* RealmDatabaseWriteTests.swift in Sources */, - 45368A1B2ABB2D850028A570 /* LocalizableStringsRepositoryTests.swift in Sources */, 45CBDA092BA3930B0007DEC8 /* MockLaunchCountRepository.swift in Sources */, + 45EB68E12C334D84008A5FF2 /* MockLocalizationServices.swift in Sources */, 45368A182ABB2D850028A570 /* TestRealmObject.swift in Sources */, 453E2A122BC8C8810047D4C6 /* GetToolsRepositoryTests.swift in Sources */, 45368A192ABB2D850028A570 /* DeepLinkingServiceTests.swift in Sources */, 455280B4295F65C300672A1B /* LanguageDirectionDomainModel.swift in Sources */, + 45CC43012C38982800B0D82B /* MockLocaleLanguageRegionName.swift in Sources */, 45A45CED2731B8C5005D4E74 /* MobileContentBackgroundImageRenderer.swift in Sources */, - 45368A1C2ABB2D850028A570 /* LocalizationServicesTests.swift in Sources */, + 450ADC452BECFE2F00988455 /* MockAppLanguagesRepositorySync.swift in Sources */, 457719AF2BE00FA600E7E4D8 /* MockDeviceSystemLanguage.swift in Sources */, 45CBDA0E2BA394FD0007DEC8 /* MockOnboardingTutorialViewedRepository.swift in Sources */, 45308EC52BDC317300A49D96 /* GetSpiritualConversationReadinessScaleTests.swift in Sources */, - 45CBDA142BA399750007DEC8 /* MockLocalizationServices.swift in Sources */, 45368A162ABB2D850028A570 /* LocaleTests.swift in Sources */, 45E198662BA47F4400BF14F3 /* GetDownloadToolProgressInterfaceStringsRepositoryTests.swift in Sources */, 450FB8872BFBA0E80015D945 /* GetOnboardingTutorialInterfaceStringsRepositoryTests.swift in Sources */, @@ -13235,18 +13378,9 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - 4F5732981EA69CF00082035C /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4F5732991EA69CF00082035C /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; 4FAF207A1F02933900C43AFC /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( - 4FAF20791F02933900C43AFC /* Base */, 4F1F09C91F0A97B400757879 /* fr */, 4F1F09CA1F0A97BE00757879 /* ru */, 4F1F09CB1F0A97C700757879 /* ar */, @@ -13313,6 +13447,7 @@ 4555599B27B3194800A32A61 /* fi */, 4576FCD427EA0B6100CB9CC6 /* ps */, 4576FCD527EA0BC200CB9CC6 /* ckb */, + 45C493F92C334A8E001D81F0 /* Base */, ); name = Localizable.strings; sourceTree = ""; @@ -13320,7 +13455,6 @@ D4B522DB29D761B700D85213 /* Localizable.stringsdict */ = { isa = PBXVariantGroup; children = ( - D4B522DA29D761B700D85213 /* English */, D4B522DC29D761BA00D85213 /* af */, D4B522DD29D761BA00D85213 /* sq */, D4B522DE29D761BB00D85213 /* am */, @@ -13387,6 +13521,7 @@ D4B5231C29D761FC00D85213 /* uk */, D4B5231D29D761FD00D85213 /* ur */, D4B5231E29D761FD00D85213 /* vi */, + 45C493FA2C334A99001D81F0 /* Base */, ); name = Localizable.stringsdict; sourceTree = ""; @@ -13508,7 +13643,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 6.3.1; + MARKETING_VERSION = 6.3.2; OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -DDEBUG"; PRODUCT_BUNDLE_IDENTIFIER = org.cru.godtools; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -13709,7 +13844,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 6.3.1; + MARKETING_VERSION = 6.3.2; OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -DDEBUG"; PRODUCT_BUNDLE_IDENTIFIER = org.cru.godtools.beta; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -13909,7 +14044,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 6.3.1; + MARKETING_VERSION = 6.3.2; OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -DDEBUG"; PRODUCT_BUNDLE_IDENTIFIER = org.cru.godtools.beta; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -14109,7 +14244,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 6.3.1; + MARKETING_VERSION = 6.3.2; OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -DDEBUG"; PRODUCT_BUNDLE_IDENTIFIER = org.cru.godtools; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -14345,7 +14480,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 6.3.1; + MARKETING_VERSION = 6.3.2; OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -DRELEASE"; PRODUCT_BUNDLE_IDENTIFIER = org.cru.godtools; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -14543,6 +14678,14 @@ minimumVersion = 13.3.0; }; }; + 45F6718F2C3343ED0017C8C2 /* XCRemoteSwiftPackageReference "localization-services-ios" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/CruGlobal/localization-services-ios"; + requirement = { + kind = upToNextMinorVersion; + minimumVersion = 0.2.1; + }; + }; D4997E0C28D4CED800205B4C /* XCRemoteSwiftPackageReference "youtube-ios-player-helper" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/youtube/youtube-ios-player-helper.git"; @@ -14554,6 +14697,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 452556432C383C3F00AA0046 /* RealmSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 4581EAA12BFBE194008115FF /* XCRemoteSwiftPackageReference "realm-swift" */; + productName = RealmSwift; + }; 457771B129A00C5C00EA8115 /* FirebaseAnalytics */ = { isa = XCSwiftPackageProductDependency; package = 457771B029A00C5C00EA8115 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; @@ -14684,6 +14832,16 @@ package = 45F486F02B76D512004E0E27 /* XCRemoteSwiftPackageReference "Nimble" */; productName = Nimble; }; + 45F671902C3344010017C8C2 /* LocalizationServices */ = { + isa = XCSwiftPackageProductDependency; + package = 45F6718F2C3343ED0017C8C2 /* XCRemoteSwiftPackageReference "localization-services-ios" */; + productName = LocalizationServices; + }; + 45F671922C33440E0017C8C2 /* LocalizationServices */ = { + isa = XCSwiftPackageProductDependency; + package = 45F6718F2C3343ED0017C8C2 /* XCRemoteSwiftPackageReference "localization-services-ios" */; + productName = LocalizationServices; + }; D4997E0D28D4CED800205B4C /* YouTubeiOSPlayerHelper */ = { isa = XCSwiftPackageProductDependency; package = D4997E0C28D4CED800205B4C /* XCRemoteSwiftPackageReference "youtube-ios-player-helper" */; diff --git a/godtools.xcodeproj/xcshareddata/xcschemes/GodTools-Production.xcscheme b/godtools.xcodeproj/xcshareddata/xcschemes/GodTools-Production.xcscheme index bc47551780..eec77b4ddc 100644 --- a/godtools.xcodeproj/xcshareddata/xcschemes/GodTools-Production.xcscheme +++ b/godtools.xcodeproj/xcshareddata/xcschemes/GodTools-Production.xcscheme @@ -63,6 +63,16 @@ ReferencedContainer = "container:godtools.xcodeproj"> + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/godtools.xcworkspace/xcshareddata/swiftpm/Package.resolved b/godtools.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3ece0bf092..208a9336a7 100644 --- a/godtools.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/godtools.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "21bf5557cd80854fb30416658e2f4affd5f9a1aedfae78f9a812d457ecc91fbe", + "originHash" : "ea584b1bef2b49fac80bdd94fb9aac7b6ff751c9759d8b8ed77b399b7efa4bcf", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -163,6 +163,15 @@ "version" : "1.22.2" } }, + { + "identity" : "localization-services-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/CruGlobal/localization-services-ios", + "state" : { + "revision" : "6d8f7d243854e3ce7caef1de672b8b7102f8cdbd", + "version" : "0.2.1" + } + }, { "identity" : "lottie-ios", "kind" : "remoteSourceControl", diff --git a/godtools/App/AppBackgroundState.swift b/godtools/App/AppBackgroundState.swift index 28d0a5c099..d69db851d0 100644 --- a/godtools/App/AppBackgroundState.swift +++ b/godtools/App/AppBackgroundState.swift @@ -48,12 +48,7 @@ class AppBackgroundState { syncLatestToolsForFavoritedTools( downloadLatestToolsForFavoritedToolsUseCase: appDiContainer.feature.dashboard.domainLayer.getDownloadLatestToolsForFavoritedToolsUseCase() ) - - syncTranslatedLanguageNames( - translatedLanguageNameRepositorySync: appDiContainer.dataLayer.getTranslatedLanguageNameRepositorySync(), - languagesRepository: appDiContainer.dataLayer.getLanguagesRepository() - ) - + syncInitialFavoritedTools( resourcesRepository: appDiContainer.dataLayer.getResourcesRepository(), launchCountRepository: appDiContainer.dataLayer.getSharedLaunchCountRepository(), @@ -78,34 +73,6 @@ class AppBackgroundState { .store(in: &cancellables) } - private func syncTranslatedLanguageNames(translatedLanguageNameRepositorySync: TranslatedLanguageNameRepositorySync, languagesRepository: LanguagesRepository) { - - Publishers.CombineLatest( - $appLanguage.dropFirst(), - languagesRepository.getLanguagesChanged().prepend(Void()) - ) - .flatMap({ (appLanguage: AppLanguageDomainModel, languagesChanged: Void) -> AnyPublisher<(AppLanguageDomainModel, [LanguageModel]), Never> in - - return languagesRepository - .getLanguagesPublisher() - .map { (languages: [LanguageModel]) in - - return (appLanguage, languages) - } - .eraseToAnyPublisher() - }) - .map { (appLanguage: AppLanguageDomainModel, languages: [LanguageModel]) in - - translatedLanguageNameRepositorySync - .syncTranslatedLanguageNamesPublisher(translateInLanguage: appLanguage, languages: languages) - } - .receive(on: DispatchQueue.main) - .sink { _ in - - } - .store(in: &cancellables) - } - private func syncInitialFavoritedTools(resourcesRepository: ResourcesRepository, launchCountRepository: LaunchCountRepository, storeInitialFavoritedToolsUseCase: StoreInitialFavoritedToolsUseCase) { Publishers.CombineLatest( diff --git a/godtools/App/DependencyContainer/AppDataLayerDependencies.swift b/godtools/App/DependencyContainer/AppDataLayerDependencies.swift index d940f055b0..b99745e1e7 100644 --- a/godtools/App/DependencyContainer/AppDataLayerDependencies.swift +++ b/godtools/App/DependencyContainer/AppDataLayerDependencies.swift @@ -8,6 +8,7 @@ import Foundation import SocialAuthentication +import LocalizationServices class AppDataLayerDependencies { @@ -146,12 +147,6 @@ class AppDataLayerDependencies { return sharedInfoPlist } - func getLanguageSettingsRepository() -> LanguageSettingsRepository { - return LanguageSettingsRepository( - cache: LanguageSettingsCache() - ) - } - func getLanguagesRepository() -> LanguagesRepository { let api = MobileContentLanguagesApi( @@ -170,6 +165,12 @@ class AppDataLayerDependencies { ) } + func getLocalizationLanguageNameRepository() -> LocalizationLanguageNameRepository { + return LocalizationLanguageNameRepository( + localizationServices: getLocalizationServices() + ) + } + func getLocalizationServices() -> LocalizationServices { return LocalizationServices(localizableStringsFilesBundle: Bundle.main) } @@ -298,28 +299,9 @@ class AppDataLayerDependencies { ) } - private func getTranslatedLanguageNameCache() -> RealmTranslatedLanguageNameCache { - return RealmTranslatedLanguageNameCache(realmDatabase: sharedRealmDatabase) - } - - func getTranslatedLanguageNameRepository() -> TranslatedLanguageNameRepository { - return TranslatedLanguageNameRepository( - getTranslatedLanguageName: getTranslatedLanguageName(), - cache: getTranslatedLanguageNameCache() - ) - } - - func getTranslatedLanguageNameRepositorySync() -> TranslatedLanguageNameRepositorySync { - return TranslatedLanguageNameRepositorySync( - realmDatabase: sharedRealmDatabase, - getTranslatedLanguageName: getTranslatedLanguageName(), - cache: getTranslatedLanguageNameCache() - ) - } - - private func getTranslatedLanguageName() -> GetTranslatedLanguageName { + func getTranslatedLanguageName() -> GetTranslatedLanguageName { return GetTranslatedLanguageName( - localizationServices: getLocalizationServices(), + localizationLanguageNameRepository: getLocalizationLanguageNameRepository(), localeLanguageName: LocaleLanguageName(), localeRegionName: LocaleLanguageRegionName(), localeScriptName: LocaleLanguageScriptName() @@ -349,7 +331,7 @@ class AppDataLayerDependencies { localizationServices: getLocalizationServices(), resourcesRepository: getResourcesRepository(), languagesRepository: getLanguagesRepository(), - translatedLanguageNameRepository: getTranslatedLanguageNameRepository() + getTranslatedLanguageName: getTranslatedLanguageName() ) } diff --git a/godtools/App/Features/Account/Data-DomainInterface/GetDeleteAccountInterfaceStringsRepository.swift b/godtools/App/Features/Account/Data-DomainInterface/GetDeleteAccountInterfaceStringsRepository.swift index 2989226a36..0f7bf72188 100644 --- a/godtools/App/Features/Account/Data-DomainInterface/GetDeleteAccountInterfaceStringsRepository.swift +++ b/godtools/App/Features/Account/Data-DomainInterface/GetDeleteAccountInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetDeleteAccountInterfaceStringsRepository: GetDeleteAccountInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Account/Data-DomainInterface/GetDeleteAccountProgressInterfaceStringsRepository.swift b/godtools/App/Features/Account/Data-DomainInterface/GetDeleteAccountProgressInterfaceStringsRepository.swift index 963982706e..4360f27a17 100644 --- a/godtools/App/Features/Account/Data-DomainInterface/GetDeleteAccountProgressInterfaceStringsRepository.swift +++ b/godtools/App/Features/Account/Data-DomainInterface/GetDeleteAccountProgressInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetDeleteAccountProgressInterfaceStringsRepository: GetDeleteAccountProgressInterfaceStringsInterface { diff --git a/godtools/App/Features/Account/Data-DomainInterface/GetSocialCreateAccountInterfaceStringsRepository.swift b/godtools/App/Features/Account/Data-DomainInterface/GetSocialCreateAccountInterfaceStringsRepository.swift index c870f27ce8..3d103cf289 100644 --- a/godtools/App/Features/Account/Data-DomainInterface/GetSocialCreateAccountInterfaceStringsRepository.swift +++ b/godtools/App/Features/Account/Data-DomainInterface/GetSocialCreateAccountInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetSocialCreateAccountInterfaceStringsRepository: GetSocialCreateAccountInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Account/Data-DomainInterface/GetSocialSignInInterfaceStringsRepository.swift b/godtools/App/Features/Account/Data-DomainInterface/GetSocialSignInInterfaceStringsRepository.swift index 05dbbaa3cc..810d4f9de1 100644 --- a/godtools/App/Features/Account/Data-DomainInterface/GetSocialSignInInterfaceStringsRepository.swift +++ b/godtools/App/Features/Account/Data-DomainInterface/GetSocialSignInInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetSocialSignInInterfaceStringsRepository: GetSocialSignInInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetAppLanguagesInterfaceStringsRepository.swift b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetAppLanguagesInterfaceStringsRepository.swift index b71c2a138e..a85b5a6960 100644 --- a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetAppLanguagesInterfaceStringsRepository.swift +++ b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetAppLanguagesInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetAppLanguagesInterfaceStringsRepository: GetAppLanguagesInterfaceStringsRepositoryInterface { @@ -19,11 +20,9 @@ class GetAppLanguagesInterfaceStringsRepository: GetAppLanguagesInterfaceStrings } func getStringsPublisher(translateInLanguage: AppLanguageDomainModel) -> AnyPublisher { - - let stringsFileType: LocalizableStringsFileType = .strings - + let interfaceStrings = AppLanguagesInterfaceStringsDomainModel( - navTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: translateInLanguage, key: AppLanguageStringKeys.AppLanguages.navTitle.rawValue, fileType: stringsFileType) + navTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: translateInLanguage, key: AppLanguageStringKeys.AppLanguages.navTitle.rawValue) ) return Just(interfaceStrings) diff --git a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetAppLanguagesListRepository.swift b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetAppLanguagesListRepository.swift index 1ae3eaa617..d2a0c7a0e8 100644 --- a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetAppLanguagesListRepository.swift +++ b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetAppLanguagesListRepository.swift @@ -12,12 +12,12 @@ import Combine class GetAppLanguagesListRepository: GetAppLanguagesListRepositoryInterface { private let appLanguagesRepository: AppLanguagesRepository - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName - init(appLanguagesRepository: AppLanguagesRepository, translatedLanguageNameRepository: TranslatedLanguageNameRepository) { + init(appLanguagesRepository: AppLanguagesRepository, getTranslatedLanguageName: GetTranslatedLanguageName) { self.appLanguagesRepository = appLanguagesRepository - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName } func getLanguagesPublisher(appLanguage: AppLanguageDomainModel) -> AnyPublisher<[AppLanguageListItemDomainModel], Never> { @@ -27,8 +27,8 @@ class GetAppLanguagesListRepository: GetAppLanguagesListRepositoryInterface { let appLanguagesList: [AppLanguageListItemDomainModel] = languages.map { (languageDataModel: AppLanguageDataModel) in - let languageNameTranslatedInOwnLanguage: String = self.translatedLanguageNameRepository.getLanguageName(language: languageDataModel, translatedInLanguage: languageDataModel.languageId) - let languageNameTranslatedInCurrentAppLanguage: String = self.translatedLanguageNameRepository.getLanguageName(language: languageDataModel, translatedInLanguage: appLanguage) + let languageNameTranslatedInOwnLanguage: String = self.getTranslatedLanguageName.getLanguageName(language: languageDataModel, translatedInLanguage: languageDataModel.languageId) + let languageNameTranslatedInCurrentAppLanguage: String = self.getTranslatedLanguageName.getLanguageName(language: languageDataModel, translatedInLanguage: appLanguage) return AppLanguageListItemDomainModel( language: languageDataModel.languageId, diff --git a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetConfirmAppLanguageInterfaceStringsRepository.swift b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetConfirmAppLanguageInterfaceStringsRepository.swift index 114631364c..792efce0fc 100644 --- a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetConfirmAppLanguageInterfaceStringsRepository.swift +++ b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetConfirmAppLanguageInterfaceStringsRepository.swift @@ -8,16 +8,17 @@ import Foundation import Combine +import LocalizationServices class GetConfirmAppLanguageInterfaceStringsRepository: GetConfirmAppLanguageInterfaceStringsRepositoryInterface { private let localizationServices: LocalizationServices - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName - init(localizationServices: LocalizationServices, translatedLanguageNameRepository: TranslatedLanguageNameRepository) { + init(localizationServices: LocalizationServices, getTranslatedLanguageName: GetTranslatedLanguageName) { self.localizationServices = localizationServices - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName } func getStringsPublisher(translateInAppLanguage: AppLanguageDomainModel, selectedLanguage: AppLanguageDomainModel) -> AnyPublisher { @@ -39,7 +40,7 @@ class GetConfirmAppLanguageInterfaceStringsRepository: GetConfirmAppLanguageInte let formatString = localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "languageSettings.confirmAppLanguage.message") - let languageName = translatedLanguageNameRepository.getLanguageName(language: selectedLanguage, translatedInLanguage: localeId) + let languageName = getTranslatedLanguageName.getLanguageName(language: selectedLanguage, translatedInLanguage: localeId) let languageNameAttributed = NSAttributedString( string: languageName, attributes: [NSAttributedString.Key.foregroundColor: ColorPalette.gtBlue.uiColor] diff --git a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadableLanguagesInterfaceStringsRepository.swift b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadableLanguagesInterfaceStringsRepository.swift index febed4d9ea..76b2d7d6ef 100644 --- a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadableLanguagesInterfaceStringsRepository.swift +++ b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadableLanguagesInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetDownloadableLanguagesInterfaceStringsRepository: GetDownloadableLanguagesInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadableLanguagesListRepository.swift b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadableLanguagesListRepository.swift index 86928676e6..11afdcb360 100644 --- a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadableLanguagesListRepository.swift +++ b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadableLanguagesListRepository.swift @@ -8,21 +8,22 @@ import Foundation import Combine +import LocalizationServices class GetDownloadableLanguagesListRepository: GetDownloadableLanguagesListRepositoryInterface { private let languagesRepository: LanguagesRepository private let downloadedLanguagesRepository: DownloadedLanguagesRepository - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName private let resourcesRepository: ResourcesRepository private let localizationServices: LocalizationServices private let sortDate: Date = Date() - init(languagesRepository: LanguagesRepository, downloadedLanguagesRepository: DownloadedLanguagesRepository, translatedLanguageNameRepository: TranslatedLanguageNameRepository, resourcesRepository: ResourcesRepository, localizationServices: LocalizationServices) { + init(languagesRepository: LanguagesRepository, downloadedLanguagesRepository: DownloadedLanguagesRepository, getTranslatedLanguageName: GetTranslatedLanguageName, resourcesRepository: ResourcesRepository, localizationServices: LocalizationServices) { self.languagesRepository = languagesRepository self.downloadedLanguagesRepository = downloadedLanguagesRepository - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName self.resourcesRepository = resourcesRepository self.localizationServices = localizationServices } @@ -42,11 +43,11 @@ class GetDownloadableLanguagesListRepository: GetDownloadableLanguagesListReposi return nil } - let languageNameInOwnLanguage = self.translatedLanguageNameRepository.getLanguageName( + let languageNameInOwnLanguage = self.getTranslatedLanguageName.getLanguageName( language: language, translatedInLanguage: language.code ) - let languageNameInAppLanguage = self.translatedLanguageNameRepository.getLanguageName( + let languageNameInAppLanguage = self.getTranslatedLanguageName.getLanguageName( language: language, translatedInLanguage: currentAppLanguage ) @@ -94,8 +95,7 @@ extension GetDownloadableLanguagesListRepository { let formatString = localizationServices.stringForLocaleElseSystemElseEnglish( localeIdentifier: localeId, - key: ToolStringKeys.ToolFilter.toolsAvailableText.rawValue, - fileType: .stringsdict + key: ToolStringKeys.ToolFilter.toolsAvailableText.rawValue ) return String(format: formatString, locale: Locale(identifier: localeId), numberOfTools) diff --git a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadedLanguagesListRepository.swift b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadedLanguagesListRepository.swift index cdf079bcf1..2d15337fe9 100644 --- a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadedLanguagesListRepository.swift +++ b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetDownloadedLanguagesListRepository.swift @@ -13,13 +13,13 @@ class GetDownloadedLanguagesListRepository: GetDownloadedLanguagesListRepository private let languagesRepository: LanguagesRepository private let downloadedLanguagesRepository: DownloadedLanguagesRepository - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName - init(languagesRepository: LanguagesRepository, downloadedLanguagesRepository: DownloadedLanguagesRepository, translatedLanguageNameRepository: TranslatedLanguageNameRepository) { + init(languagesRepository: LanguagesRepository, downloadedLanguagesRepository: DownloadedLanguagesRepository, getTranslatedLanguageName: GetTranslatedLanguageName) { self.languagesRepository = languagesRepository self.downloadedLanguagesRepository = downloadedLanguagesRepository - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName } func getDownloadedLanguagesPublisher(currentAppLanguage: AppLanguageDomainModel) -> AnyPublisher<[DownloadedLanguageListItemDomainModel], Never> { @@ -38,11 +38,11 @@ class GetDownloadedLanguagesListRepository: GetDownloadedLanguagesListRepository return self.languagesRepository.getLanguages(ids: downloadedLanguageIds).map { language in - let languageNameInOwnLanguage = self.translatedLanguageNameRepository.getLanguageName( + let languageNameInOwnLanguage = self.getTranslatedLanguageName.getLanguageName( language: language, translatedInLanguage: language.code ) - let languageNameInAppLanguage = self.translatedLanguageNameRepository.getLanguageName( + let languageNameInAppLanguage = self.getTranslatedLanguageName.getLanguageName( language: language, translatedInLanguage: currentAppLanguage ) diff --git a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetLanguageSettingsInterfaceStringsRepository.swift b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetLanguageSettingsInterfaceStringsRepository.swift index 6a1cb4527c..592c80f8f3 100644 --- a/godtools/App/Features/AppLanguage/Data-DomainInterface/GetLanguageSettingsInterfaceStringsRepository.swift +++ b/godtools/App/Features/AppLanguage/Data-DomainInterface/GetLanguageSettingsInterfaceStringsRepository.swift @@ -8,17 +8,18 @@ import Foundation import Combine +import LocalizationServices class GetLanguageSettingsInterfaceStringsRepository: GetLanguageSettingsInterfaceStringsRepositoryInterface { private let localizationServices: LocalizationServicesInterface - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName private let appLanguagesRepository: AppLanguagesRepository - init(localizationServices: LocalizationServicesInterface, translatedLanguageNameRepository: TranslatedLanguageNameRepository, appLanguagesRepository: AppLanguagesRepository) { + init(localizationServices: LocalizationServicesInterface, getTranslatedLanguageName: GetTranslatedLanguageName, appLanguagesRepository: AppLanguagesRepository) { self.localizationServices = localizationServices - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName self.appLanguagesRepository = appLanguagesRepository } @@ -26,33 +27,43 @@ class GetLanguageSettingsInterfaceStringsRepository: GetLanguageSettingsInterfac let localeId: String = translateInAppLanguage - let interfaceStrings = LanguageSettingsInterfaceStringsDomainModel( - navTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsNavTitle.key, fileType: .strings), - appInterfaceLanguageTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsAppInterfaceTitle.key, fileType: .strings), - numberOfAppLanguagesAvailable: getNumberOfAppLanguagesAvailableString(translateInAppLanguage: translateInAppLanguage), - setAppLanguageMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsAppInterfaceMessage.key, fileType: .strings), - chooseAppLanguageButtonTitle: translatedLanguageNameRepository.getLanguageName(language: translateInAppLanguage, translatedInLanguage: translateInAppLanguage), - toolLanguagesAvailableOfflineTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineTitle.key, fileType: .strings), - downloadToolsForOfflineMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineMessage.key, fileType: .strings), - editDownloadedLanguagesButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineEditDownloadedLanguagesButtonTitle.key, fileType: .strings) - ) - - return Just(interfaceStrings) + return getNumberOfAppLanguagesAvailableStringPublisher(translateInAppLanguage: translateInAppLanguage) + .map { (numberOfAppLanguagesInterfaceString: String) in + + let interfaceStrings = LanguageSettingsInterfaceStringsDomainModel( + navTitle: self.localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsNavTitle.key), + appInterfaceLanguageTitle: self.localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsAppInterfaceTitle.key), + numberOfAppLanguagesAvailable: numberOfAppLanguagesInterfaceString, + setAppLanguageMessage: self.localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsAppInterfaceMessage.key), + chooseAppLanguageButtonTitle: self.getTranslatedLanguageName.getLanguageName(language: translateInAppLanguage, translatedInLanguage: translateInAppLanguage), + toolLanguagesAvailableOfflineTitle: self.localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineTitle.key), + downloadToolsForOfflineMessage: self.localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineMessage.key), + editDownloadedLanguagesButtonTitle: self.localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineEditDownloadedLanguagesButtonTitle.key) + ) + + return interfaceStrings + } .eraseToAnyPublisher() } - private func getNumberOfAppLanguagesAvailableString(translateInAppLanguage: AppLanguageDomainModel) -> String { - - let numberOfAppLanguages: Int = appLanguagesRepository.getNumberOfCachedLanguages() + private func getNumberOfAppLanguagesAvailableStringPublisher(translateInAppLanguage: AppLanguageDomainModel) -> AnyPublisher { - let localizedNumberOfLanguagesAvailable = localizationServices.stringForLocaleElseSystemElseEnglish( - localeIdentifier: translateInAppLanguage, - key: LocalizableStringDictKeys.languageSettingsAppLanguageNumberAvailable.key, - fileType: .stringsdict - ) - - let stringLocaleFormat = String(format: localizedNumberOfLanguagesAvailable, locale: Locale(identifier: translateInAppLanguage), numberOfAppLanguages) - - return stringLocaleFormat + return appLanguagesRepository.observeNumberOfAppLanguagesPublisher() + .map { (numberOfAppLanguages: Int) in + + let localizedNumberOfLanguagesAvailable = self.localizationServices.stringForLocaleElseSystemElseEnglish( + localeIdentifier: translateInAppLanguage, + key: LocalizableStringDictKeys.languageSettingsAppLanguageNumberAvailable.key + ) + + let stringLocaleFormat = String( + format: localizedNumberOfLanguagesAvailable, + locale: Locale(identifier: translateInAppLanguage), + numberOfAppLanguages + ) + + return stringLocaleFormat + } + .eraseToAnyPublisher() } } diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Api/AppLanguageCodable.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Api/AppLanguageCodable.swift new file mode 100644 index 0000000000..3bdb75e84d --- /dev/null +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Api/AppLanguageCodable.swift @@ -0,0 +1,48 @@ +// +// AppLanguageCodable.swift +// godtools +// +// Created by Levi Eggert on 5/7/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +struct AppLanguageCodable: Codable { + + private static let languageDirectionLeftToRight: String = "ltr" + private static let languageDirectionRightToLeft: String = "rtl" + + let languageCode: String + let languageDirectionValue: String + let languageScriptCode: String? + + enum RootKeys: String, CodingKey { + case languageCode = "language_code" + case languageDirection = "language_direction" + case languageScriptCode = "language_script_code" + } + + init(languageCode: String, languageDirection: AppLanguageDataModel.Direction, languageScriptCode: String?) { + + self.languageCode = languageCode + self.languageDirectionValue = languageDirection == .rightToLeft ? AppLanguageCodable.languageDirectionRightToLeft : AppLanguageCodable.languageDirectionLeftToRight + self.languageScriptCode = languageScriptCode + } + + init(from decoder: Decoder) throws { + + let container = try decoder.container(keyedBy: RootKeys.self) + + languageCode = try container.decode(String.self, forKey: .languageCode) + languageDirectionValue = try container.decode(String.self, forKey: .languageDirection) + languageScriptCode = try container.decodeIfPresent(String.self, forKey: .languageScriptCode) + } +} + +extension AppLanguageCodable: AppLanguageDataModelInterface { + + var languageDirection: AppLanguageDataModel.Direction { + return languageDirectionValue == AppLanguageCodable.languageDirectionRightToLeft ? .rightToLeft : .leftToRight + } +} diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Api/AppLanguagesApi.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Api/AppLanguagesApi.swift new file mode 100644 index 0000000000..238b7b7429 --- /dev/null +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Api/AppLanguagesApi.swift @@ -0,0 +1,41 @@ +// +// AppLanguagesApi.swift +// godtools +// +// Created by Levi Eggert on 5/7/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import Combine + +class AppLanguagesApi { + + init() { + + } + + func getAppLanguagesPublisher() -> AnyPublisher<[AppLanguageCodable], Error> { + + let allAppLanguages: [AppLanguageCodable] = [ + AppLanguageCodable(languageCode: "am", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "ar", languageDirection: .rightToLeft, languageScriptCode: nil), + AppLanguageCodable(languageCode: "en", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "es", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "pt", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "fr", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "id", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hans"), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hant"), + AppLanguageCodable(languageCode: "hi", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "ru", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "vi", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "lv", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "bn", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "ur", languageDirection: .rightToLeft, languageScriptCode: nil) + ] + + return Just(allAppLanguages).setFailureType(to: Error.self) + .eraseToAnyPublisher() + } +} diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguageDataModel.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguageDataModel.swift index 3bea17893d..6e34728ca6 100644 --- a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguageDataModel.swift +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguageDataModel.swift @@ -8,19 +8,24 @@ import Foundation -struct AppLanguageDataModel { +struct AppLanguageDataModel: AppLanguageDataModelInterface { let languageCode: String let languageDirection: AppLanguageDataModel.Direction let languageScriptCode: String? - var languageId: BCP47LanguageIdentifier { + init(languageCode: String, languageDirection: AppLanguageDataModel.Direction, languageScriptCode: String?) { - if let languageScriptCode = languageScriptCode, !languageScriptCode.isEmpty { - return languageCode + "-" + languageScriptCode - } + self.languageCode = languageCode + self.languageDirection = languageDirection + self.languageScriptCode = languageScriptCode + } + + init(dataModel: AppLanguageDataModelInterface) { - return languageCode + self.languageCode = dataModel.languageCode + self.languageDirection = dataModel.languageDirection + self.languageScriptCode = dataModel.languageScriptCode } } diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguageDataModelInterface.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguageDataModelInterface.swift new file mode 100644 index 0000000000..03d664282d --- /dev/null +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguageDataModelInterface.swift @@ -0,0 +1,28 @@ +// +// AppLanguageDataModelInterface.swift +// godtools +// +// Created by Levi Eggert on 5/7/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +protocol AppLanguageDataModelInterface { + + var languageCode: String { get } + var languageDirection: AppLanguageDataModel.Direction { get } + var languageScriptCode: String? { get } +} + +extension AppLanguageDataModelInterface { + + var languageId: BCP47LanguageIdentifier { + + if let languageScriptCode = languageScriptCode, !languageScriptCode.isEmpty { + return languageCode + "-" + languageScriptCode + } + + return languageCode + } +} diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguagesRepository.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguagesRepository.swift index bd11f39bd2..765aee6a6b 100644 --- a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguagesRepository.swift +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/AppLanguagesRepository.swift @@ -10,42 +10,67 @@ import Foundation import Combine class AppLanguagesRepository { + + private let cache: RealmAppLanguagesCache + private let sync: AppLanguagesRepositorySyncInterface - private let cache: AppLanguagesCache + private var cancellables: Set = Set() - init(cache: AppLanguagesCache) { + init(cache: RealmAppLanguagesCache, sync: AppLanguagesRepositorySyncInterface) { self.cache = cache + self.sync = sync } - func getNumberOfCachedLanguages() -> Int { - return cache.getNumberOfLanguages() + func observeNumberOfAppLanguagesPublisher() -> AnyPublisher { + + sync.syncPublisher() + .sink { _ in + + } + .store(in: &cancellables) + + return cache.observeChangesPublisher() + .flatMap({ (changes: Void) -> AnyPublisher in + return self.cache.getNumberOfLanguagesPublisher() + .eraseToAnyPublisher() + }) + .eraseToAnyPublisher() } - func getLanguagesPublisher() -> AnyPublisher<[AppLanguageDataModel], Never> { + func observeAppLanguagesPublisher() -> AnyPublisher<[AppLanguageDataModel], Never> { - let appLanguages: [AppLanguageDataModel] = cache.getAppLanguages() + sync.syncPublisher() + .sink { _ in + + } + .store(in: &cancellables) - return Just(appLanguages) + return cache.observeChangesPublisher() + .flatMap({ (changes: Void) -> AnyPublisher<[AppLanguageDataModel], Never> in + return self.cache.getLanguagesPublisher() + .eraseToAnyPublisher() + }) .eraseToAnyPublisher() } - func getLanguagesChangedPublisher() -> AnyPublisher { + func getLanguagesPublisher() -> AnyPublisher<[AppLanguageDataModel], Never> { - return Just(Void()) + return sync.syncPublisher() + .flatMap({ (syncCompleted: Void) -> AnyPublisher<[AppLanguageDataModel], Never> in + + return self.cache.getLanguagesPublisher() + .eraseToAnyPublisher() + }) .eraseToAnyPublisher() } func getLanguagePublisher(languageId: BCP47LanguageIdentifier) -> AnyPublisher { - return getLanguagesPublisher() - .flatMap({ (languages: [AppLanguageDataModel]) -> AnyPublisher in - - let language: AppLanguageDataModel? = languages.filter({ - $0.languageId.lowercased() == languageId.lowercased() - }).first + return sync.syncPublisher() + .flatMap({ (syncCompleted: Void) -> AnyPublisher in - return Just(language) + return self.cache.getLanguagePublisher(languageId: languageId) .eraseToAnyPublisher() }) .eraseToAnyPublisher() diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/AppLanguagesCache.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/AppLanguagesCache.swift deleted file mode 100644 index c7b8ea21a7..0000000000 --- a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/AppLanguagesCache.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// AppLanguagesCache.swift -// godtools -// -// Created by Levi Eggert on 10/23/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation - -class AppLanguagesCache { - - private let appLanguages: [AppLanguageDataModel] - - init() { - - appLanguages = [ - AppLanguageDataModel(languageCode: "ar", languageDirection: .rightToLeft, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "en", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "es", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "pt", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "fr", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "id", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hans"), - AppLanguageDataModel(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hant"), - AppLanguageDataModel(languageCode: "hi", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "ru", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "vi", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "lv", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "bn", languageDirection: .leftToRight, languageScriptCode: nil), - AppLanguageDataModel(languageCode: "ur", languageDirection: .rightToLeft, languageScriptCode: nil) - ] - } - - func getNumberOfLanguages() -> Int { - return appLanguages.count - } - - func getAppLanguages() -> [AppLanguageDataModel] { - - return appLanguages - } -} diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguage.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguage.swift new file mode 100644 index 0000000000..f32abe1f7b --- /dev/null +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguage.swift @@ -0,0 +1,51 @@ +// +// RealmAppLanguage.swift +// godtools +// +// Created by Levi Eggert on 5/2/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import RealmSwift + +class RealmAppLanguage: Object { + + @Persisted var id: String = "" + @Persisted var languageCode: String = "" + @Persisted var realmLanguageDirection: RealmAppLanguageDirection = .leftToRight + @Persisted var languageId: String = "" + @Persisted var languageScriptCode: String? + + override static func primaryKey() -> String? { + return "id" + } + + func mapFrom(dataModel: AppLanguageDataModelInterface) { + + id = dataModel.languageId + languageCode = dataModel.languageCode + languageId = dataModel.languageId + languageScriptCode = dataModel.languageScriptCode + + switch dataModel.languageDirection { + case .leftToRight: + realmLanguageDirection = .leftToRight + case .rightToLeft: + realmLanguageDirection = .rightToLeft + } + } +} + +extension RealmAppLanguage: AppLanguageDataModelInterface { + + var languageDirection: AppLanguageDataModel.Direction { + + switch realmLanguageDirection { + case .leftToRight: + return .leftToRight + case .rightToLeft: + return .rightToLeft + } + } +} diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguageDirection.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguageDirection.swift new file mode 100644 index 0000000000..8549fa84a0 --- /dev/null +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguageDirection.swift @@ -0,0 +1,15 @@ +// +// RealmAppLanguageDirection.swift +// godtools +// +// Created by Levi Eggert on 5/2/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import RealmSwift + +enum RealmAppLanguageDirection: String, PersistableEnum { + case leftToRight + case rightToLeft +} diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguagesCache.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguagesCache.swift new file mode 100644 index 0000000000..8a95cac53e --- /dev/null +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Cache/RealmAppLanguagesCache.swift @@ -0,0 +1,83 @@ +// +// RealmAppLanguagesCache.swift +// godtools +// +// Created by Levi Eggert on 5/2/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import Combine +import RealmSwift + +class RealmAppLanguagesCache { + + private let realmDatabase: RealmDatabase + + init(realmDatabase: RealmDatabase) { + + self.realmDatabase = realmDatabase + } + + func observeChangesPublisher() -> AnyPublisher { + return realmDatabase.openRealm().objects(RealmAppLanguage.self).objectWillChange + .eraseToAnyPublisher() + .prepend(Void()) + .eraseToAnyPublisher() + } + + func getNumberOfLanguagesPublisher() -> AnyPublisher { + + let count: Int = realmDatabase.openRealm() + .objects(RealmAppLanguage.self) + .count + + return Just(count) + .eraseToAnyPublisher() + } + + func getLanguagesPublisher() -> AnyPublisher<[AppLanguageDataModel], Never> { + + return realmDatabase.readObjectsPublisher(mapInBackgroundClosure: { (results: Results) -> [AppLanguageDataModel] in + + return results.map({ + AppLanguageDataModel(dataModel: $0) + }) + }) + .eraseToAnyPublisher() + } + + func getLanguagePublisher(languageId: BCP47LanguageIdentifier) -> AnyPublisher { + + return realmDatabase.readObjectPublisher(primaryKey: languageId, mapInBackgroundClosure: { (object: RealmAppLanguage?) -> AppLanguageDataModel? in + + guard let realmAppLanguage = object else { + return nil + } + + return AppLanguageDataModel(dataModel: realmAppLanguage) + }) + .eraseToAnyPublisher() + } + + func storeLanguagesPublisher(appLanguages: [AppLanguageDataModel]) -> AnyPublisher<[AppLanguageDataModel], Error> { + + return realmDatabase.writeObjectsPublisher { (realm: Realm) -> [RealmAppLanguage] in + + let realmObjects: [RealmAppLanguage] = appLanguages.map({ + let realmAppLanguage = RealmAppLanguage() + realmAppLanguage.mapFrom(dataModel: $0) + return realmAppLanguage + }) + + return realmObjects + + } mapInBackgroundClosure: { (objects: [RealmAppLanguage]) -> [AppLanguageDataModel] in + + return objects.map({ + AppLanguageDataModel(dataModel: $0) + }) + } + .eraseToAnyPublisher() + } +} diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/AppLanguagesRepositorySync.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/AppLanguagesRepositorySync.swift new file mode 100644 index 0000000000..bc5414c4dc --- /dev/null +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/AppLanguagesRepositorySync.swift @@ -0,0 +1,59 @@ +// +// AppLanguagesRepositorySync.swift +// godtools +// +// Created by Levi Eggert on 5/7/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import Combine + +class AppLanguagesRepositorySync: AppLanguagesRepositorySyncInterface { + + private let api: AppLanguagesApi + private let cache: RealmAppLanguagesCache + private let syncInvalidator: SyncInvalidator = SyncInvalidatorManager.getInvalidator(id: String(describing: AppLanguagesRepositorySync.self), timeInterval: .minutes(minute: 15)) + + init(api: AppLanguagesApi, cache: RealmAppLanguagesCache) { + + self.api = api + self.cache = cache + } + + func syncPublisher() -> AnyPublisher { + + guard syncInvalidator.shouldSync else { + return Just(Void()) + .eraseToAnyPublisher() + } + + return api.getAppLanguagesPublisher() + .catch({ (error: Error) in + return Just([]) + .eraseToAnyPublisher() + }) + .flatMap({ (appLanguages: [AppLanguageCodable]) -> AnyPublisher<[AppLanguageDataModel], Never> in + + let dataModels: [AppLanguageDataModel] = appLanguages.map({ + AppLanguageDataModel(dataModel: $0) + }) + + return Just(dataModels) + .eraseToAnyPublisher() + }) + .flatMap({ (appLanguages: [AppLanguageDataModel]) -> AnyPublisher in + + return self.cache.storeLanguagesPublisher(appLanguages: appLanguages) + .catch({ _ in + return Just([]) + .eraseToAnyPublisher() + }) + .map { _ in + return () + } + .eraseToAnyPublisher() + }) + .eraseToAnyPublisher() + } +} diff --git a/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/AppLanguagesRepositorySyncInterface.swift b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/AppLanguagesRepositorySyncInterface.swift new file mode 100644 index 0000000000..ec94ad3f96 --- /dev/null +++ b/godtools/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/AppLanguagesRepositorySyncInterface.swift @@ -0,0 +1,15 @@ +// +// AppLanguagesRepositorySyncInterface.swift +// godtools +// +// Created by Levi Eggert on 5/7/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import Combine + +protocol AppLanguagesRepositorySyncInterface { + + func syncPublisher() -> AnyPublisher +} diff --git a/godtools/App/Features/AppLanguage/Data/UserAppLanguageRepository/Cache/RealmUserAppLanguageCache.swift b/godtools/App/Features/AppLanguage/Data/UserAppLanguageRepository/Cache/RealmUserAppLanguageCache.swift index 2c7823820a..fee922b611 100644 --- a/godtools/App/Features/AppLanguage/Data/UserAppLanguageRepository/Cache/RealmUserAppLanguageCache.swift +++ b/godtools/App/Features/AppLanguage/Data/UserAppLanguageRepository/Cache/RealmUserAppLanguageCache.swift @@ -47,6 +47,16 @@ class RealmUserAppLanguageCache { .eraseToAnyPublisher() } + func deleteLanguage() -> Error? { + + return realmDatabase.deleteObjects( + realm: realmDatabase.openRealm(), + type: RealmUserAppLanguage.self, + primaryKeyPath: #keyPath(RealmUserAppLanguage.id), + primaryKeys: [RealmUserAppLanguageCache.sharedUserId] + ) + } + func storeLanguage(languageId: BCP47LanguageIdentifier) { let realm: Realm = realmDatabase.openRealm() diff --git a/godtools/App/Features/AppLanguage/DependencyContainer/AppLanguageFeatureDataLayerDependencies.swift b/godtools/App/Features/AppLanguage/DependencyContainer/AppLanguageFeatureDataLayerDependencies.swift index 07e24ebdea..d043b5426b 100644 --- a/godtools/App/Features/AppLanguage/DependencyContainer/AppLanguageFeatureDataLayerDependencies.swift +++ b/godtools/App/Features/AppLanguage/DependencyContainer/AppLanguageFeatureDataLayerDependencies.swift @@ -19,12 +19,18 @@ class AppLanguageFeatureDataLayerDependencies { // MARK: - Data Layer Classes - private func getAppLanguagesCache() -> AppLanguagesCache { - return AppLanguagesCache() - } - - func getAppLanguagesRepository() -> AppLanguagesRepository { - return AppLanguagesRepository(cache: getAppLanguagesCache()) + func getAppLanguagesRepository(realmDatabase: RealmDatabase? = nil, sync: AppLanguagesRepositorySyncInterface? = nil) -> AppLanguagesRepository { + + let cache = RealmAppLanguagesCache( + realmDatabase: realmDatabase ?? coreDataLayer.getSharedRealmDatabase() + ) + + let sync: AppLanguagesRepositorySyncInterface = sync ?? AppLanguagesRepositorySync(api: AppLanguagesApi(), cache: cache) + + return AppLanguagesRepository( + cache: cache, + sync: sync + ) } private func getDownloadedLanguagesRepository() -> DownloadedLanguagesRepository { @@ -40,19 +46,13 @@ class AppLanguageFeatureDataLayerDependencies { ) } - func getUserAppLanguageCache() -> RealmUserAppLanguageCache { - return RealmUserAppLanguageCache( - realmDatabase: coreDataLayer.getSharedRealmDatabase() - ) - } - private func getRealmDownloadedLanguagesCache() -> RealmDownloadedLanguagesCache { return RealmDownloadedLanguagesCache(realmDatabase: coreDataLayer.getSharedRealmDatabase()) } func getUserAppLanguageRepository() -> UserAppLanguageRepository { return UserAppLanguageRepository( - cache: getUserAppLanguageCache() + cache: RealmUserAppLanguageCache(realmDatabase: coreDataLayer.getSharedRealmDatabase()) ) } @@ -67,7 +67,7 @@ class AppLanguageFeatureDataLayerDependencies { func getAppLanguagesListRepositoryInterface() -> GetAppLanguagesListRepositoryInterface { return GetAppLanguagesListRepository( appLanguagesRepository: getAppLanguagesRepository(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository() + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName() ) } @@ -86,7 +86,7 @@ class AppLanguageFeatureDataLayerDependencies { func getConfirmAppLanguageInterfaceStringsRepositoryInterface() -> GetConfirmAppLanguageInterfaceStringsRepositoryInterface { return GetConfirmAppLanguageInterfaceStringsRepository( localizationServices: coreDataLayer.getLocalizationServices(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository() + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName() ) } @@ -100,7 +100,7 @@ class AppLanguageFeatureDataLayerDependencies { return GetDownloadableLanguagesListRepository( languagesRepository: coreDataLayer.getLanguagesRepository(), downloadedLanguagesRepository: getDownloadedLanguagesRepository(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository(), + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName(), resourcesRepository: coreDataLayer.getResourcesRepository(), localizationServices: coreDataLayer.getLocalizationServices() ) @@ -110,7 +110,7 @@ class AppLanguageFeatureDataLayerDependencies { return GetDownloadedLanguagesListRepository( languagesRepository: coreDataLayer.getLanguagesRepository(), downloadedLanguagesRepository: getDownloadedLanguagesRepository(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository() + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName() ) } @@ -125,7 +125,7 @@ class AppLanguageFeatureDataLayerDependencies { func getLanguageSettingsInterfaceStringsRepositoryInterface() -> GetLanguageSettingsInterfaceStringsRepositoryInterface { return GetLanguageSettingsInterfaceStringsRepository( localizationServices: coreDataLayer.getLocalizationServices(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository(), + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName(), appLanguagesRepository: getAppLanguagesRepository() ) } diff --git a/godtools/App/Features/Articles/Presentation/ArticleCategories/ArticleCategoriesViewModel.swift b/godtools/App/Features/Articles/Presentation/ArticleCategories/ArticleCategoriesViewModel.swift index 7f49b00353..2ebaa84598 100644 --- a/godtools/App/Features/Articles/Presentation/ArticleCategories/ArticleCategoriesViewModel.swift +++ b/godtools/App/Features/Articles/Presentation/ArticleCategories/ArticleCategoriesViewModel.swift @@ -10,6 +10,7 @@ import Foundation import RealmSwift import GodToolsToolParser import Combine +import LocalizationServices class ArticleCategoriesViewModel { diff --git a/godtools/App/Features/Articles/Presentation/Articles/ArticlesViewModel.swift b/godtools/App/Features/Articles/Presentation/Articles/ArticlesViewModel.swift index 2f377fb1e7..186c8fcbe3 100644 --- a/godtools/App/Features/Articles/Presentation/Articles/ArticlesViewModel.swift +++ b/godtools/App/Features/Articles/Presentation/Articles/ArticlesViewModel.swift @@ -9,6 +9,7 @@ import Foundation import GodToolsToolParser import Combine +import LocalizationServices class ArticlesViewModel: NSObject { diff --git a/godtools/App/Features/Articles/Presentation/ArticlesErrorMessage/ArticlesErrorMessageViewModel.swift b/godtools/App/Features/Articles/Presentation/ArticlesErrorMessage/ArticlesErrorMessageViewModel.swift index 574273d695..ac8f6e1c42 100644 --- a/godtools/App/Features/Articles/Presentation/ArticlesErrorMessage/ArticlesErrorMessageViewModel.swift +++ b/godtools/App/Features/Articles/Presentation/ArticlesErrorMessage/ArticlesErrorMessageViewModel.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices class ArticlesErrorMessageViewModel { diff --git a/godtools/App/Features/Articles/Presentation/DownloadArticlesError/DownloadArticlesErrorViewModel.swift b/godtools/App/Features/Articles/Presentation/DownloadArticlesError/DownloadArticlesErrorViewModel.swift index c1e58ae858..4b51c9cef3 100644 --- a/godtools/App/Features/Articles/Presentation/DownloadArticlesError/DownloadArticlesErrorViewModel.swift +++ b/godtools/App/Features/Articles/Presentation/DownloadArticlesError/DownloadArticlesErrorViewModel.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices class DownloadArticlesErrorViewModel { diff --git a/godtools/App/Features/Articles/Presentation/LoadingArticle/LoadingArticleViewModel.swift b/godtools/App/Features/Articles/Presentation/LoadingArticle/LoadingArticleViewModel.swift index ee3cf211e9..d4fa11b053 100644 --- a/godtools/App/Features/Articles/Presentation/LoadingArticle/LoadingArticleViewModel.swift +++ b/godtools/App/Features/Articles/Presentation/LoadingArticle/LoadingArticleViewModel.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class LoadingArticleViewModel: ObservableObject { diff --git a/godtools/App/Features/Dashboard/Data-DomainInterface/GetDashboardInterfaceStringsRepository.swift b/godtools/App/Features/Dashboard/Data-DomainInterface/GetDashboardInterfaceStringsRepository.swift index 27d99512d9..f52595ee3b 100644 --- a/godtools/App/Features/Dashboard/Data-DomainInterface/GetDashboardInterfaceStringsRepository.swift +++ b/godtools/App/Features/Dashboard/Data-DomainInterface/GetDashboardInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetDashboardInterfaceStringsRepository: GetDashboardInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Dashboard/Data-DomainInterface/GetToolsInterfaceStringsRepository.swift b/godtools/App/Features/Dashboard/Data-DomainInterface/GetToolsInterfaceStringsRepository.swift index 7ac21a0f82..97cd54a08e 100644 --- a/godtools/App/Features/Dashboard/Data-DomainInterface/GetToolsInterfaceStringsRepository.swift +++ b/godtools/App/Features/Dashboard/Data-DomainInterface/GetToolsInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolsInterfaceStringsRepository: GetToolsInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Dashboard/Presentation/ChooseYourOwnAdventure/ChooseYourOwnAdventureViewModel.swift b/godtools/App/Features/Dashboard/Presentation/ChooseYourOwnAdventure/ChooseYourOwnAdventureViewModel.swift index 28f12a361f..d5618cc802 100644 --- a/godtools/App/Features/Dashboard/Presentation/ChooseYourOwnAdventure/ChooseYourOwnAdventureViewModel.swift +++ b/godtools/App/Features/Dashboard/Presentation/ChooseYourOwnAdventure/ChooseYourOwnAdventureViewModel.swift @@ -22,7 +22,7 @@ class ChooseYourOwnAdventureViewModel: MobileContentPagesViewModel { @Published var hidesHomeButton: Bool = false @Published var hidesBackButton: Bool = true - init(flowDelegate: FlowDelegate, renderer: MobileContentRenderer, initialPage: MobileContentPagesPage?, resourcesRepository: ResourcesRepository, translationsRepository: TranslationsRepository, mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, translatedLanguageNameRepository: TranslatedLanguageNameRepository, trainingTipsEnabled: Bool, incrementUserCounterUseCase: IncrementUserCounterUseCase, selectedLanguageIndex: Int?) { + init(flowDelegate: FlowDelegate, renderer: MobileContentRenderer, initialPage: MobileContentPagesPage?, resourcesRepository: ResourcesRepository, translationsRepository: TranslationsRepository, mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, getTranslatedLanguageName: GetTranslatedLanguageName, trainingTipsEnabled: Bool, incrementUserCounterUseCase: IncrementUserCounterUseCase, selectedLanguageIndex: Int?) { self.flowDelegate = flowDelegate @@ -38,7 +38,7 @@ class ChooseYourOwnAdventureViewModel: MobileContentPagesViewModel { languageFont = FontLibrary.systemUIFont(size: 14, weight: .regular) - super.init(renderer: renderer, initialPage: initialPage, resourcesRepository: resourcesRepository, translationsRepository: translationsRepository, mobileContentEventAnalytics: mobileContentEventAnalytics, getCurrentAppLanguageUseCase: getCurrentAppLanguageUseCase, translatedLanguageNameRepository: translatedLanguageNameRepository, initialPageRenderingType: .chooseYourOwnAdventure, trainingTipsEnabled: trainingTipsEnabled, incrementUserCounterUseCase: incrementUserCounterUseCase, selectedLanguageIndex: selectedLanguageIndex) + super.init(renderer: renderer, initialPage: initialPage, resourcesRepository: resourcesRepository, translationsRepository: translationsRepository, mobileContentEventAnalytics: mobileContentEventAnalytics, getCurrentAppLanguageUseCase: getCurrentAppLanguageUseCase, getTranslatedLanguageName: getTranslatedLanguageName, initialPageRenderingType: .chooseYourOwnAdventure, trainingTipsEnabled: trainingTipsEnabled, incrementUserCounterUseCase: incrementUserCounterUseCase, selectedLanguageIndex: selectedLanguageIndex) } deinit { diff --git a/godtools/App/Features/Dashboard/Presentation/Dashboard/DashboardView.swift b/godtools/App/Features/Dashboard/Presentation/Dashboard/DashboardView.swift index 0c42f5b80a..265212ec84 100644 --- a/godtools/App/Features/Dashboard/Presentation/Dashboard/DashboardView.swift +++ b/godtools/App/Features/Dashboard/Presentation/Dashboard/DashboardView.swift @@ -109,7 +109,8 @@ struct DashboardView_Previews: PreviewProvider { flowDelegate: MockFlowDelegate(), dashboardPresentationLayerDependencies: DashboardPresentationLayerDependencies(appDiContainer: appDiContainer, flowDelegate: MockFlowDelegate()), getCurrentAppLanguageUseCase: appDiContainer.feature.appLanguage.domainLayer.getCurrentAppLanguageUseCase(), - viewDashboardUseCase: appDiContainer.feature.dashboard.domainLayer.getViewDashboardUseCase() + viewDashboardUseCase: appDiContainer.feature.dashboard.domainLayer.getViewDashboardUseCase(), + dashboardTabObserver: CurrentValueSubject(.favorites) ) return viewModel diff --git a/godtools/App/Features/Dashboard/Presentation/Dashboard/DashboardViewModel.swift b/godtools/App/Features/Dashboard/Presentation/Dashboard/DashboardViewModel.swift index 76ebb15462..f1b5245c1e 100644 --- a/godtools/App/Features/Dashboard/Presentation/Dashboard/DashboardViewModel.swift +++ b/godtools/App/Features/Dashboard/Presentation/Dashboard/DashboardViewModel.swift @@ -28,7 +28,7 @@ class DashboardViewModel: ObservableObject { @Published var toolsButtonTitle: String = "" @Published var currentTab: Int = 0 - init(startingTab: DashboardTabTypeDomainModel, flowDelegate: FlowDelegate, dashboardPresentationLayerDependencies: DashboardPresentationLayerDependencies, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, viewDashboardUseCase: ViewDashboardUseCase) { + init(startingTab: DashboardTabTypeDomainModel, flowDelegate: FlowDelegate, dashboardPresentationLayerDependencies: DashboardPresentationLayerDependencies, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, viewDashboardUseCase: ViewDashboardUseCase, dashboardTabObserver: CurrentValueSubject) { self.flowDelegate = flowDelegate self.dashboardPresentationLayerDependencies = dashboardPresentationLayerDependencies @@ -59,6 +59,15 @@ class DashboardViewModel: ObservableObject { self?.setStartingTabIfNeeded(startingTab: startingTab, tabs: self?.tabs ?? Array()) } .store(in: &cancellables) + + $currentTab.eraseToAnyPublisher() + .sink { [weak self] currentTab in + + guard let self = self else { return } + + dashboardTabObserver.send(self.tabs[currentTab]) + } + .store(in: &cancellables) } deinit { diff --git a/godtools/App/Features/Dashboard/Presentation/Tool/ToolViewModel.swift b/godtools/App/Features/Dashboard/Presentation/Tool/ToolViewModel.swift index fdcb356300..0a376bb2d2 100644 --- a/godtools/App/Features/Dashboard/Presentation/Tool/ToolViewModel.swift +++ b/godtools/App/Features/Dashboard/Presentation/Tool/ToolViewModel.swift @@ -16,11 +16,14 @@ class ToolViewModel: MobileContentPagesViewModel { private let tractRemoteShareSubscriber: TractRemoteShareSubscriber private let resourceViewsService: ResourceViewsService private let trackActionAnalyticsUseCase: TrackActionAnalyticsUseCase + private let persistUserToolLanguageSettingsUseCase: PersistUserToolLanguageSettingsUseCase private let toolOpenedAnalytics: ToolOpenedAnalytics private let liveShareStream: String? private var cancellables: Set = Set() private var remoteShareIsActive: Bool = false + private var shouldPersistToolSettings: Bool = false + private var toolSettingsObserver: ToolSettingsObserver? private weak var flowDelegate: FlowDelegate? @@ -30,7 +33,7 @@ class ToolViewModel: MobileContentPagesViewModel { @Published var hidesRemoteShareIsActive: Bool = true - init(flowDelegate: FlowDelegate, renderer: MobileContentRenderer, tractRemoteSharePublisher: TractRemoteSharePublisher, tractRemoteShareSubscriber: TractRemoteShareSubscriber, resourceViewsService: ResourceViewsService, trackActionAnalyticsUseCase: TrackActionAnalyticsUseCase, resourcesRepository: ResourcesRepository, translationsRepository: TranslationsRepository, mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, translatedLanguageNameRepository: TranslatedLanguageNameRepository, toolOpenedAnalytics: ToolOpenedAnalytics, liveShareStream: String?, initialPage: MobileContentPagesPage?, trainingTipsEnabled: Bool, incrementUserCounterUseCase: IncrementUserCounterUseCase, selectedLanguageIndex: Int?) { + init(flowDelegate: FlowDelegate, renderer: MobileContentRenderer, tractRemoteSharePublisher: TractRemoteSharePublisher, tractRemoteShareSubscriber: TractRemoteShareSubscriber, resourceViewsService: ResourceViewsService, trackActionAnalyticsUseCase: TrackActionAnalyticsUseCase, resourcesRepository: ResourcesRepository, translationsRepository: TranslationsRepository, mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, getTranslatedLanguageName: GetTranslatedLanguageName, toolOpenedAnalytics: ToolOpenedAnalytics, liveShareStream: String?, initialPage: MobileContentPagesPage?, trainingTipsEnabled: Bool, incrementUserCounterUseCase: IncrementUserCounterUseCase, selectedLanguageIndex: Int?, persistUserToolLanguageSettingsUseCase: PersistUserToolLanguageSettingsUseCase, shouldPersistToolSettings: Bool) { self.flowDelegate = flowDelegate self.tractRemoteSharePublisher = tractRemoteSharePublisher @@ -39,6 +42,8 @@ class ToolViewModel: MobileContentPagesViewModel { self.trackActionAnalyticsUseCase = trackActionAnalyticsUseCase self.toolOpenedAnalytics = toolOpenedAnalytics self.liveShareStream = liveShareStream + self.persistUserToolLanguageSettingsUseCase = persistUserToolLanguageSettingsUseCase + self.shouldPersistToolSettings = shouldPersistToolSettings let primaryManifest: Manifest = renderer.pageRenderers[0].manifest @@ -52,7 +57,7 @@ class ToolViewModel: MobileContentPagesViewModel { languageFont = FontLibrary.systemUIFont(size: 14, weight: .regular) - super.init(renderer: renderer, initialPage: initialPage, resourcesRepository: resourcesRepository, translationsRepository: translationsRepository, mobileContentEventAnalytics: mobileContentEventAnalytics, getCurrentAppLanguageUseCase: getCurrentAppLanguageUseCase, translatedLanguageNameRepository: translatedLanguageNameRepository, initialPageRenderingType: .visiblePages, trainingTipsEnabled: trainingTipsEnabled, incrementUserCounterUseCase: incrementUserCounterUseCase, selectedLanguageIndex: selectedLanguageIndex) + super.init(renderer: renderer, initialPage: initialPage, resourcesRepository: resourcesRepository, translationsRepository: translationsRepository, mobileContentEventAnalytics: mobileContentEventAnalytics, getCurrentAppLanguageUseCase: getCurrentAppLanguageUseCase, getTranslatedLanguageName: getTranslatedLanguageName, initialPageRenderingType: .visiblePages, trainingTipsEnabled: trainingTipsEnabled, incrementUserCounterUseCase: incrementUserCounterUseCase, selectedLanguageIndex: selectedLanguageIndex) setupBinding() } @@ -161,25 +166,8 @@ class ToolViewModel: MobileContentPagesViewModel { data: trackTappedLanguageData ) } -} - -// MARK: - Inputs - -extension ToolViewModel { - @objc func homeTapped() { - - let isScreenSharing: Bool = remoteShareIsActive - - flowDelegate?.navigate(step: .homeTappedFromTool(isScreenSharing: isScreenSharing)) - } - - @objc func backTapped() { - - flowDelegate?.navigate(step: .backTappedFromTool) - } - - @objc func toolSettingsTapped() { + private func createToolSettingsObserver() -> ToolSettingsObserver { let languages = ToolSettingsLanguages( primaryLanguageId: languages[0].id, @@ -194,7 +182,7 @@ extension ToolViewModel { trainingTipsEnabled: trainingTipsEnabled, tractRemoteSharePublisher: tractRemoteSharePublisher ) - + toolSettingsObserver.$languages .receive(on: DispatchQueue.main) .sink { [weak self] (languages: ToolSettingsLanguages) in @@ -215,6 +203,57 @@ extension ToolViewModel { } .store(in: &cancellables) + if shouldPersistToolSettings { + + toolSettingsObserver.$languages + .map { [weak self] (languages: ToolSettingsLanguages) in + + guard let self = self else { + return Just(false) + .eraseToAnyPublisher() + } + + return self.persistUserToolLanguageSettingsUseCase + .persistUserToolSettingsPublisher( + with: renderer.value.resource.id, + primaryLanguageId: languages.primaryLanguageId, + parallelLanguageId: languages.parallelLanguageId, + selectedLanguageId: languages.selectedLanguageId + ) + } + .switchToLatest() + .receive(on: DispatchQueue.main) + .sink(receiveValue: { _ in + + }) + .store(in: &cancellables) + } + + self.toolSettingsObserver = toolSettingsObserver + return toolSettingsObserver + } +} + +// MARK: - Inputs + +extension ToolViewModel { + + @objc func homeTapped() { + + let isScreenSharing: Bool = remoteShareIsActive + + flowDelegate?.navigate(step: .homeTappedFromTool(isScreenSharing: isScreenSharing)) + } + + @objc func backTapped() { + + flowDelegate?.navigate(step: .backTappedFromTool) + } + + @objc func toolSettingsTapped() { + + let toolSettingsObserver = createToolSettingsObserver() + trackActionAnalyticsUseCase .trackAction( screenName: analyticsScreenName, @@ -243,6 +282,18 @@ extension ToolViewModel { pagePositions: pagePositions ) + if let toolSettingsObserver = toolSettingsObserver { + + let languages = toolSettingsObserver.languages + self.toolSettingsObserver?.languages = ToolSettingsLanguages( + primaryLanguageId: languages.primaryLanguageId, + parallelLanguageId: languages.parallelLanguageId, + selectedLanguageId: tappedLanguage.id + ) + } else { + _ = createToolSettingsObserver() + } + trackLanguageTapped(tappedLanguage: tappedLanguage) } diff --git a/godtools/App/Features/DownloadToolProgress/Data-DomainInterface/GetDownloadToolProgressInterfaceStringsRepository.swift b/godtools/App/Features/DownloadToolProgress/Data-DomainInterface/GetDownloadToolProgressInterfaceStringsRepository.swift index 6fb51c85a6..f07a5f7852 100644 --- a/godtools/App/Features/DownloadToolProgress/Data-DomainInterface/GetDownloadToolProgressInterfaceStringsRepository.swift +++ b/godtools/App/Features/DownloadToolProgress/Data-DomainInterface/GetDownloadToolProgressInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetDownloadToolProgressInterfaceStringsRepository: GetDownloadToolProgressInterfaceStringsRepositoryInterface { @@ -25,7 +26,6 @@ class GetDownloadToolProgressInterfaceStringsRepository: GetDownloadToolProgress func getStringsPublisher(toolId: String?, translateInAppLanguage: AppLanguageDomainModel) -> AnyPublisher { let localeId: String = translateInAppLanguage - let stringsFileType: LocalizableStringsFileType = .strings let resource: ResourceModel? @@ -49,10 +49,10 @@ class GetDownloadToolProgressInterfaceStringsRepository: GetDownloadToolProgress let downloadMessage: String if toolCanBeFavorited && !toolIsFavorited { - downloadMessage = localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "loading_unfavorited_tool", fileType: stringsFileType) + downloadMessage = localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "loading_unfavorited_tool") } else { - downloadMessage = localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "loading_favorited_tool", fileType: stringsFileType) + downloadMessage = localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "loading_favorited_tool") } let interfaceStrings = DownloadToolProgressInterfaceStringsDomainModel( diff --git a/godtools/App/Features/Favorites/Data-DomainInterface/GetAllYourFavoritedToolsInterfaceStringsRepository.swift b/godtools/App/Features/Favorites/Data-DomainInterface/GetAllYourFavoritedToolsInterfaceStringsRepository.swift index 83233b184b..192494614c 100644 --- a/godtools/App/Features/Favorites/Data-DomainInterface/GetAllYourFavoritedToolsInterfaceStringsRepository.swift +++ b/godtools/App/Features/Favorites/Data-DomainInterface/GetAllYourFavoritedToolsInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetAllYourFavoritedToolsInterfaceStringsRepository: GetAllYourFavoritedToolsInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Favorites/Data-DomainInterface/GetConfirmRemoveToolFromFavoritesInterfaceStringsRepository.swift b/godtools/App/Features/Favorites/Data-DomainInterface/GetConfirmRemoveToolFromFavoritesInterfaceStringsRepository.swift index bf24d421c5..82cbfb432a 100644 --- a/godtools/App/Features/Favorites/Data-DomainInterface/GetConfirmRemoveToolFromFavoritesInterfaceStringsRepository.swift +++ b/godtools/App/Features/Favorites/Data-DomainInterface/GetConfirmRemoveToolFromFavoritesInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetConfirmRemoveToolFromFavoritesInterfaceStringsRepository: GetConfirmRemoveToolFromFavoritesInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Favorites/Data-DomainInterface/GetFavoritesInterfaceStringsRepository.swift b/godtools/App/Features/Favorites/Data-DomainInterface/GetFavoritesInterfaceStringsRepository.swift index 1b4df56d77..cb53518ba9 100644 --- a/godtools/App/Features/Favorites/Data-DomainInterface/GetFavoritesInterfaceStringsRepository.swift +++ b/godtools/App/Features/Favorites/Data-DomainInterface/GetFavoritesInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetFavoritesInterfaceStringsRepository: GetFavoritesInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/GlobalActivity/Data-DomainInterface/GetGlobalActivityThisWeekRepository.swift b/godtools/App/Features/GlobalActivity/Data-DomainInterface/GetGlobalActivityThisWeekRepository.swift index cfd5564ad6..a0001d4ee4 100644 --- a/godtools/App/Features/GlobalActivity/Data-DomainInterface/GetGlobalActivityThisWeekRepository.swift +++ b/godtools/App/Features/GlobalActivity/Data-DomainInterface/GetGlobalActivityThisWeekRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetGlobalActivityThisWeekRepository: GetGlobalActivityThisWeekRepositoryInterface { diff --git a/godtools/App/Features/LearnToShareTool/Data-DomainInterface/GetLearnToShareToolInterfaceStringsRepository.swift b/godtools/App/Features/LearnToShareTool/Data-DomainInterface/GetLearnToShareToolInterfaceStringsRepository.swift index c11ae532fb..fc097f7b25 100644 --- a/godtools/App/Features/LearnToShareTool/Data-DomainInterface/GetLearnToShareToolInterfaceStringsRepository.swift +++ b/godtools/App/Features/LearnToShareTool/Data-DomainInterface/GetLearnToShareToolInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetLearnToShareToolInterfaceStringsRepository: GetLearnToShareToolInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/LearnToShareTool/Data-DomainInterface/GetLearnToShareToolTutorialItemsRepository.swift b/godtools/App/Features/LearnToShareTool/Data-DomainInterface/GetLearnToShareToolTutorialItemsRepository.swift index 000baebfa8..93661da592 100644 --- a/godtools/App/Features/LearnToShareTool/Data-DomainInterface/GetLearnToShareToolTutorialItemsRepository.swift +++ b/godtools/App/Features/LearnToShareTool/Data-DomainInterface/GetLearnToShareToolTutorialItemsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetLearnToShareToolTutorialItemsRepository: GetLearnToShareToolTutorialItemsRepositoryInterface { diff --git a/godtools/App/Features/LessonEvaluation/Data-DomainInterface/GetLessonEvaluationInterfaceStringsRepository.swift b/godtools/App/Features/LessonEvaluation/Data-DomainInterface/GetLessonEvaluationInterfaceStringsRepository.swift index a30cefc055..cc9b23cf5d 100644 --- a/godtools/App/Features/LessonEvaluation/Data-DomainInterface/GetLessonEvaluationInterfaceStringsRepository.swift +++ b/godtools/App/Features/LessonEvaluation/Data-DomainInterface/GetLessonEvaluationInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetLessonEvaluationInterfaceStringsRepository: GetLessonEvaluationInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Lessons/Data-DomainInterface/GetLessonsInterfaceStringsRepository.swift b/godtools/App/Features/Lessons/Data-DomainInterface/GetLessonsInterfaceStringsRepository.swift index 30357c7966..4d0f21e3f3 100644 --- a/godtools/App/Features/Lessons/Data-DomainInterface/GetLessonsInterfaceStringsRepository.swift +++ b/godtools/App/Features/Lessons/Data-DomainInterface/GetLessonsInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetLessonsInterfaceStringsRepository: GetLessonsInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Lessons/Presentation/Lesson/LessonViewModel.swift b/godtools/App/Features/Lessons/Presentation/Lesson/LessonViewModel.swift index 300758f839..a41659192d 100644 --- a/godtools/App/Features/Lessons/Presentation/Lesson/LessonViewModel.swift +++ b/godtools/App/Features/Lessons/Presentation/Lesson/LessonViewModel.swift @@ -14,11 +14,11 @@ class LessonViewModel: MobileContentPagesViewModel { let progress: ObservableValue> = ObservableValue(value: AnimatableValue(value: 0, animated: false)) - init(flowDelegate: FlowDelegate, renderer: MobileContentRenderer, resource: ResourceModel, primaryLanguage: LanguageDomainModel, initialPage: MobileContentPagesPage?, resourcesRepository: ResourcesRepository, translationsRepository: TranslationsRepository, mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, translatedLanguageNameRepository: TranslatedLanguageNameRepository, trainingTipsEnabled: Bool, incrementUserCounterUseCase: IncrementUserCounterUseCase) { + init(flowDelegate: FlowDelegate, renderer: MobileContentRenderer, resource: ResourceModel, primaryLanguage: LanguageDomainModel, initialPage: MobileContentPagesPage?, resourcesRepository: ResourcesRepository, translationsRepository: TranslationsRepository, mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, getTranslatedLanguageName: GetTranslatedLanguageName, trainingTipsEnabled: Bool, incrementUserCounterUseCase: IncrementUserCounterUseCase) { self.flowDelegate = flowDelegate - super.init(renderer: renderer,initialPage: initialPage, resourcesRepository: resourcesRepository, translationsRepository: translationsRepository, mobileContentEventAnalytics: mobileContentEventAnalytics, getCurrentAppLanguageUseCase: getCurrentAppLanguageUseCase, translatedLanguageNameRepository: translatedLanguageNameRepository, initialPageRenderingType: .visiblePages, trainingTipsEnabled: trainingTipsEnabled, incrementUserCounterUseCase: incrementUserCounterUseCase, selectedLanguageIndex: nil) + super.init(renderer: renderer,initialPage: initialPage, resourcesRepository: resourcesRepository, translationsRepository: translationsRepository, mobileContentEventAnalytics: mobileContentEventAnalytics, getCurrentAppLanguageUseCase: getCurrentAppLanguageUseCase, getTranslatedLanguageName: getTranslatedLanguageName, initialPageRenderingType: .visiblePages, trainingTipsEnabled: trainingTipsEnabled, incrementUserCounterUseCase: incrementUserCounterUseCase, selectedLanguageIndex: nil) } deinit { diff --git a/godtools/App/Features/Menu/Data-DomainInterface/GetAccountInterfaceStringsRepository.swift b/godtools/App/Features/Menu/Data-DomainInterface/GetAccountInterfaceStringsRepository.swift index 118a8fac38..feee284e45 100644 --- a/godtools/App/Features/Menu/Data-DomainInterface/GetAccountInterfaceStringsRepository.swift +++ b/godtools/App/Features/Menu/Data-DomainInterface/GetAccountInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetAccountInterfaceStringsRepository: GetAccountInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Menu/Data-DomainInterface/GetMenuInterfaceStringsRepository.swift b/godtools/App/Features/Menu/Data-DomainInterface/GetMenuInterfaceStringsRepository.swift index 12dd4b9429..650634bbdc 100644 --- a/godtools/App/Features/Menu/Data-DomainInterface/GetMenuInterfaceStringsRepository.swift +++ b/godtools/App/Features/Menu/Data-DomainInterface/GetMenuInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetMenuInterfaceStringsRepository: GetMenuInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Menu/Domain/UseCases/GetUserAccountDetailsUseCase/GetUserAccountDetailsUseCase.swift b/godtools/App/Features/Menu/Domain/UseCases/GetUserAccountDetailsUseCase/GetUserAccountDetailsUseCase.swift index 3c18800157..e1c72454f2 100644 --- a/godtools/App/Features/Menu/Domain/UseCases/GetUserAccountDetailsUseCase/GetUserAccountDetailsUseCase.swift +++ b/godtools/App/Features/Menu/Domain/UseCases/GetUserAccountDetailsUseCase/GetUserAccountDetailsUseCase.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetUserAccountDetailsUseCase { diff --git a/godtools/App/Features/Menu/Domain/UseCases/GetUserActivityBadgeUseCase/GetUserActivityBadgeUseCase.swift b/godtools/App/Features/Menu/Domain/UseCases/GetUserActivityBadgeUseCase/GetUserActivityBadgeUseCase.swift index 84499d3029..b970e63510 100644 --- a/godtools/App/Features/Menu/Domain/UseCases/GetUserActivityBadgeUseCase/GetUserActivityBadgeUseCase.swift +++ b/godtools/App/Features/Menu/Domain/UseCases/GetUserActivityBadgeUseCase/GetUserActivityBadgeUseCase.swift @@ -9,6 +9,7 @@ import Foundation import GodToolsToolParser import SwiftUI +import LocalizationServices class GetUserActivityBadgeUseCase { @@ -80,8 +81,7 @@ class GetUserActivityBadgeUseCase { let formatString = localizationServices.stringForLocaleElseEnglish( localeIdentifier: translatedInAppLanguage.localeId, - key: stringLocalizationKey, - fileType: .stringsdict + key: stringLocalizationKey ) let badgeText: String = String(format: formatString, locale: Locale(identifier: translatedInAppLanguage), progressTarget) diff --git a/godtools/App/Features/Menu/Domain/UseCases/GetUserActivityStatsUseCase/GetUserActivityStatsUseCase.swift b/godtools/App/Features/Menu/Domain/UseCases/GetUserActivityStatsUseCase/GetUserActivityStatsUseCase.swift index 9efab45000..33947cb4c2 100644 --- a/godtools/App/Features/Menu/Domain/UseCases/GetUserActivityStatsUseCase/GetUserActivityStatsUseCase.swift +++ b/godtools/App/Features/Menu/Domain/UseCases/GetUserActivityStatsUseCase/GetUserActivityStatsUseCase.swift @@ -9,6 +9,7 @@ import Foundation import GodToolsToolParser import SwiftUI +import LocalizationServices class GetUserActivityStatsUseCase { @@ -72,8 +73,7 @@ class GetUserActivityStatsUseCase { let formatString = localizationServices.stringForLocaleElseEnglish( localeIdentifier: translatedInAppLanguage.localeId, - key: stringKey, - fileType: .stringsdict + key: stringKey ) return String(format: formatString, locale: Locale(identifier: translatedInAppLanguage), activityCount) diff --git a/godtools/App/Features/Menu/Presentation/WebContent/Models/AskAQuestionWebContent.swift b/godtools/App/Features/Menu/Presentation/WebContent/Models/AskAQuestionWebContent.swift index f9be0bccbd..6c55c81319 100644 --- a/godtools/App/Features/Menu/Presentation/WebContent/Models/AskAQuestionWebContent.swift +++ b/godtools/App/Features/Menu/Presentation/WebContent/Models/AskAQuestionWebContent.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices struct AskAQuestionWebContent: WebContentType { diff --git a/godtools/App/Features/Menu/Presentation/WebContent/Models/CopyrightInfoWebContent.swift b/godtools/App/Features/Menu/Presentation/WebContent/Models/CopyrightInfoWebContent.swift index 4b66e377b4..b513577996 100644 --- a/godtools/App/Features/Menu/Presentation/WebContent/Models/CopyrightInfoWebContent.swift +++ b/godtools/App/Features/Menu/Presentation/WebContent/Models/CopyrightInfoWebContent.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices struct CopyrightInfoWebContent: WebContentType { diff --git a/godtools/App/Features/Menu/Presentation/WebContent/Models/PrivacyPolicyWebContent.swift b/godtools/App/Features/Menu/Presentation/WebContent/Models/PrivacyPolicyWebContent.swift index 5be4ef06e7..a9176af513 100644 --- a/godtools/App/Features/Menu/Presentation/WebContent/Models/PrivacyPolicyWebContent.swift +++ b/godtools/App/Features/Menu/Presentation/WebContent/Models/PrivacyPolicyWebContent.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices struct PrivacyPolicyWebContent: WebContentType { diff --git a/godtools/App/Features/Menu/Presentation/WebContent/Models/ReportABugWebContent.swift b/godtools/App/Features/Menu/Presentation/WebContent/Models/ReportABugWebContent.swift index a63f9226fe..47d8cb7509 100644 --- a/godtools/App/Features/Menu/Presentation/WebContent/Models/ReportABugWebContent.swift +++ b/godtools/App/Features/Menu/Presentation/WebContent/Models/ReportABugWebContent.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices struct ReportABugWebContent: WebContentType { diff --git a/godtools/App/Features/Menu/Presentation/WebContent/Models/SendFeedbackWebContent.swift b/godtools/App/Features/Menu/Presentation/WebContent/Models/SendFeedbackWebContent.swift index 381ae3db9c..07acf115a5 100644 --- a/godtools/App/Features/Menu/Presentation/WebContent/Models/SendFeedbackWebContent.swift +++ b/godtools/App/Features/Menu/Presentation/WebContent/Models/SendFeedbackWebContent.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices struct SendFeedbackWebContent: WebContentType { diff --git a/godtools/App/Features/Menu/Presentation/WebContent/Models/ShareAStoryWithUsWebContent.swift b/godtools/App/Features/Menu/Presentation/WebContent/Models/ShareAStoryWithUsWebContent.swift index dbc8e06deb..3126982052 100644 --- a/godtools/App/Features/Menu/Presentation/WebContent/Models/ShareAStoryWithUsWebContent.swift +++ b/godtools/App/Features/Menu/Presentation/WebContent/Models/ShareAStoryWithUsWebContent.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices struct ShareAStoryWithUsWebContent: WebContentType { diff --git a/godtools/App/Features/Menu/Presentation/WebContent/Models/TermsOfUseWebContent.swift b/godtools/App/Features/Menu/Presentation/WebContent/Models/TermsOfUseWebContent.swift index 22c5a87b83..83ad136e7a 100644 --- a/godtools/App/Features/Menu/Presentation/WebContent/Models/TermsOfUseWebContent.swift +++ b/godtools/App/Features/Menu/Presentation/WebContent/Models/TermsOfUseWebContent.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices struct TermsOfUseWebContent: WebContentType { diff --git a/godtools/App/Features/Onboarding/Data-DomainInterface/GetOnboardingTutorialInterfaceStringsRepository.swift b/godtools/App/Features/Onboarding/Data-DomainInterface/GetOnboardingTutorialInterfaceStringsRepository.swift index 9e44461a2e..5ff2534fa6 100644 --- a/godtools/App/Features/Onboarding/Data-DomainInterface/GetOnboardingTutorialInterfaceStringsRepository.swift +++ b/godtools/App/Features/Onboarding/Data-DomainInterface/GetOnboardingTutorialInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetOnboardingTutorialInterfaceStringsRepository: GetOnboardingTutorialInterfaceStringsRepositoryInterface { @@ -21,21 +22,20 @@ class GetOnboardingTutorialInterfaceStringsRepository: GetOnboardingTutorialInte func getStringsPublisher(appLanguage: AppLanguageDomainModel) -> AnyPublisher { let localeId: String = appLanguage - let stringsFileType: LocalizableStringsFileType = .strings let interfaceStrings = OnboardingTutorialInterfaceStringsDomainModel( - chooseAppLanguageButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.chooseLanguageButton.title", fileType: stringsFileType), - beginTutorialButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.beginButton.title", fileType: stringsFileType), - nextTutorialPageButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.nextButton.title", fileType: stringsFileType), - endTutorialButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.getStartedButton.title", fileType: stringsFileType), - readyForEveryConversationTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.0.title", fileType: stringsFileType), - readyForEveryConversationVideoLinkTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.0.videoLink.title", fileType: stringsFileType), - prepareForMomentsThatMatterTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.2.title", fileType: stringsFileType), - prepareForMomentsThatMatterMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.2.message", fileType: stringsFileType), - talkWithGodAboutAnyoneTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.1.title", fileType: stringsFileType), - talkWithGodAboutAnyoneMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.1.message", fileType: stringsFileType), - helpSomeoneDiscoverJesusTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.3.title", fileType: stringsFileType), - helpSomeoneDiscoverJesusMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.3.message", fileType: stringsFileType) + chooseAppLanguageButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.chooseLanguageButton.title"), + beginTutorialButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.beginButton.title"), + nextTutorialPageButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.nextButton.title"), + endTutorialButtonTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.getStartedButton.title"), + readyForEveryConversationTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.0.title"), + readyForEveryConversationVideoLinkTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.0.videoLink.title"), + prepareForMomentsThatMatterTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.2.title"), + prepareForMomentsThatMatterMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.2.message"), + talkWithGodAboutAnyoneTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.1.title"), + talkWithGodAboutAnyoneMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.1.message"), + helpSomeoneDiscoverJesusTitle: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.3.title"), + helpSomeoneDiscoverJesusMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: localeId, key: "onboardingTutorial.3.message") ) return Just(interfaceStrings) diff --git a/godtools/App/Features/PersistUserToolLanguageSettings/Data-DomainInterface/PersistUserToolLanguageSettingsRepository.swift b/godtools/App/Features/PersistUserToolLanguageSettings/Data-DomainInterface/PersistUserToolLanguageSettingsRepository.swift new file mode 100644 index 0000000000..39de19aa9a --- /dev/null +++ b/godtools/App/Features/PersistUserToolLanguageSettings/Data-DomainInterface/PersistUserToolLanguageSettingsRepository.swift @@ -0,0 +1,32 @@ +// +// PersistUserToolLanguageSettingsRepository.swift +// godtools +// +// Created by Rachael Skeath on 5/30/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import Combine + +class PersistUserToolLanguageSettingsRepository: PersistUserToolLanguageSettingsRepositoryInterface { + + private let userToolSettingsRepository: UserToolSettingsRepository + + init(userToolSettingsRepository: UserToolSettingsRepository) { + self.userToolSettingsRepository = userToolSettingsRepository + } + + func persistUserToolLanguageSettingsPublisher(toolId: String, primaryLanguageId: String, parallelLanguageId: String?, selectedLanguageId: String) -> AnyPublisher { + + userToolSettingsRepository.storeUserToolSettings( + toolId: toolId, + primaryLanguageId: primaryLanguageId, + parallelLanguageId: parallelLanguageId, + selectedLanguageId: selectedLanguageId + ) + + return Just(true) + .eraseToAnyPublisher() + } +} diff --git a/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/Cache/RealmUserToolSettings.swift b/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/Cache/RealmUserToolSettings.swift new file mode 100644 index 0000000000..82614e16ce --- /dev/null +++ b/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/Cache/RealmUserToolSettings.swift @@ -0,0 +1,32 @@ +// +// RealmUserToolSettings.swift +// godtools +// +// Created by Rachael Skeath on 5/30/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import RealmSwift + +class RealmUserToolSettings: Object { + + @Persisted var createdAt: Date = Date() + @Persisted var toolId: String = "" + @Persisted var primaryLanguageId: String = "" + @Persisted var parallelLanguageId: String? = nil + @Persisted var selectedLanguageId: String = "" + + override static func primaryKey() -> String? { + return "toolId" + } + + func mapFrom(dataModel: UserToolSettingsDataModel) { + + createdAt = dataModel.createdAt + toolId = dataModel.toolId + primaryLanguageId = dataModel.primaryLanguageId + parallelLanguageId = dataModel.parallelLanguageId + selectedLanguageId = dataModel.selectedLanguageId + } +} diff --git a/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/Cache/RealmUserToolSettingsCache.swift b/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/Cache/RealmUserToolSettingsCache.swift new file mode 100644 index 0000000000..783c935a46 --- /dev/null +++ b/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/Cache/RealmUserToolSettingsCache.swift @@ -0,0 +1,55 @@ +// +// RealmUserToolSettingsCache.swift +// godtools +// +// Created by Rachael Skeath on 5/30/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import RealmSwift +import Combine + +class RealmUserToolSettingsCache { + + private let realmDatabase: RealmDatabase + + init(realmDatabase: RealmDatabase) { + self.realmDatabase = realmDatabase + } + + func storeUserToolSettings(dataModel: UserToolSettingsDataModel) { + + let realm: Realm = realmDatabase.openRealm() + + let realmToolSettings = RealmUserToolSettings() + realmToolSettings.mapFrom(dataModel: dataModel) + + do { + + try realm.write { + realm.add(realmToolSettings, update: .modified) + } + } + catch let error { + print(error) + } + } + + func getUserToolSettings(toolId: String) -> UserToolSettingsDataModel? { + + let realm: Realm = realmDatabase.openRealm() + + guard let realmToolSettings = realm.object(ofType: RealmUserToolSettings.self, forPrimaryKey: toolId) else { + return nil + } + + return UserToolSettingsDataModel(realmObject: realmToolSettings) + } + + func getUserToolSettingsPublisher(toolId: String) -> AnyPublisher { + + return Just(getUserToolSettings(toolId: toolId)) + .eraseToAnyPublisher() + } +} diff --git a/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/UserToolSettingsDataModel.swift b/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/UserToolSettingsDataModel.swift new file mode 100644 index 0000000000..eac414dd80 --- /dev/null +++ b/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/UserToolSettingsDataModel.swift @@ -0,0 +1,34 @@ +// +// UserToolSettingsDataModel.swift +// godtools +// +// Created by Rachael Skeath on 5/30/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +struct UserToolSettingsDataModel { + + let createdAt: Date + let toolId: String + let primaryLanguageId: String + let parallelLanguageId: String? + let selectedLanguageId: String + + init(toolId: String, primaryLanguageId: String, parallelLanguageId: String?, selectedLanguageId: String) { + createdAt = Date() + self.toolId = toolId + self.primaryLanguageId = primaryLanguageId + self.parallelLanguageId = parallelLanguageId + self.selectedLanguageId = selectedLanguageId + } + + init(realmObject: RealmUserToolSettings) { + createdAt = realmObject.createdAt + toolId = realmObject.toolId + primaryLanguageId = realmObject.primaryLanguageId + parallelLanguageId = realmObject.parallelLanguageId + selectedLanguageId = realmObject.selectedLanguageId + } +} diff --git a/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/UserToolSettingsRepository.swift b/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/UserToolSettingsRepository.swift new file mode 100644 index 0000000000..ce6950081d --- /dev/null +++ b/godtools/App/Features/PersistUserToolLanguageSettings/Data/UserToolSettingsRepository/UserToolSettingsRepository.swift @@ -0,0 +1,41 @@ +// +// UserToolSettingsRepository.swift +// godtools +// +// Created by Rachael Skeath on 5/30/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import Combine + +class UserToolSettingsRepository { + + private let cache: RealmUserToolSettingsCache + + init(cache: RealmUserToolSettingsCache) { + self.cache = cache + } + + func storeUserToolSettings(toolId: String, primaryLanguageId: String, parallelLanguageId: String?, selectedLanguageId: String) { + + let dataModel = UserToolSettingsDataModel( + toolId: toolId, + primaryLanguageId: primaryLanguageId, + parallelLanguageId: parallelLanguageId, + selectedLanguageId: selectedLanguageId + ) + + cache.storeUserToolSettings(dataModel: dataModel) + } + + func getUserToolSettings(toolId: String) -> UserToolSettingsDataModel? { + + return cache.getUserToolSettings(toolId: toolId) + } + + func getUserToolSettingsPublisher(toolId: String) -> AnyPublisher { + + return cache.getUserToolSettingsPublisher(toolId: toolId) + } +} diff --git a/godtools/App/Features/PersistUserToolLanguageSettings/Domain/Interfaces/PersistUserToolLanguageSettingsRepositoryInterface.swift b/godtools/App/Features/PersistUserToolLanguageSettings/Domain/Interfaces/PersistUserToolLanguageSettingsRepositoryInterface.swift new file mode 100644 index 0000000000..a30506f2ee --- /dev/null +++ b/godtools/App/Features/PersistUserToolLanguageSettings/Domain/Interfaces/PersistUserToolLanguageSettingsRepositoryInterface.swift @@ -0,0 +1,15 @@ +// +// PersistUserToolLanguageSettingsRepositoryInterface.swift +// godtools +// +// Created by Rachael Skeath on 5/30/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import Combine + +protocol PersistUserToolLanguageSettingsRepositoryInterface { + + func persistUserToolLanguageSettingsPublisher(toolId: String, primaryLanguageId: String, parallelLanguageId: String?, selectedLanguageId: String) -> AnyPublisher +} diff --git a/godtools/App/Features/PersistUserToolLanguageSettings/Domain/UseCases/PersistUserToolLanguageSettingsUseCase.swift b/godtools/App/Features/PersistUserToolLanguageSettings/Domain/UseCases/PersistUserToolLanguageSettingsUseCase.swift new file mode 100644 index 0000000000..7bd2f26c28 --- /dev/null +++ b/godtools/App/Features/PersistUserToolLanguageSettings/Domain/UseCases/PersistUserToolLanguageSettingsUseCase.swift @@ -0,0 +1,24 @@ +// +// PersistUserToolLanguageSettingsUseCase.swift +// godtools +// +// Created by Rachael Skeath on 5/29/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import Combine + +class PersistUserToolLanguageSettingsUseCase { + + private let persistUserToolLanguageSettingsRepository: PersistUserToolLanguageSettingsRepositoryInterface + + init(persistUserToolLanguageSettingsRepositoryInterface: PersistUserToolLanguageSettingsRepositoryInterface) { + self.persistUserToolLanguageSettingsRepository = persistUserToolLanguageSettingsRepositoryInterface + } + + func persistUserToolSettingsPublisher(with toolId: String, primaryLanguageId: String, parallelLanguageId: String?, selectedLanguageId: String) -> AnyPublisher { + + return persistUserToolLanguageSettingsRepository.persistUserToolLanguageSettingsPublisher(toolId: toolId, primaryLanguageId: primaryLanguageId, parallelLanguageId: parallelLanguageId, selectedLanguageId: selectedLanguageId) + } +} diff --git a/godtools/App/Features/ShareGodTools/Data-DomainInterface/GetShareGodToolsInterfaceStringsRepository.swift b/godtools/App/Features/ShareGodTools/Data-DomainInterface/GetShareGodToolsInterfaceStringsRepository.swift index 1bde3a6040..37f39f3805 100644 --- a/godtools/App/Features/ShareGodTools/Data-DomainInterface/GetShareGodToolsInterfaceStringsRepository.swift +++ b/godtools/App/Features/ShareGodTools/Data-DomainInterface/GetShareGodToolsInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetShareGodToolsInterfaceStringsRepository: GetShareGodToolsInterfaceStringsRepositoryInterface { @@ -21,7 +22,7 @@ class GetShareGodToolsInterfaceStringsRepository: GetShareGodToolsInterfaceStrin func getInterfaceStringsPublisher(translateInLanguage: AppLanguageDomainModel) -> AnyPublisher { let interfaceStrings = ShareGodToolsInterfaceStringsDomainModel( - shareMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: translateInLanguage, key: "share_god_tools_share_sheet_text", fileType: .strings) + shareMessage: localizationServices.stringForLocaleElseEnglish(localeIdentifier: translateInLanguage, key: "share_god_tools_share_sheet_text") ) return Just(interfaceStrings) diff --git a/godtools/App/Features/Shareables/Data-DomainInterface/GetReviewShareShareableInterfaceStringsRepository.swift b/godtools/App/Features/Shareables/Data-DomainInterface/GetReviewShareShareableInterfaceStringsRepository.swift index 14d81ec8a5..7f909bd425 100644 --- a/godtools/App/Features/Shareables/Data-DomainInterface/GetReviewShareShareableInterfaceStringsRepository.swift +++ b/godtools/App/Features/Shareables/Data-DomainInterface/GetReviewShareShareableInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetReviewShareShareableInterfaceStringsRepository: GetReviewShareShareableInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/SpotlightTools/Data-DomainInterface/GetSpotlightToolsRepository.swift b/godtools/App/Features/SpotlightTools/Data-DomainInterface/GetSpotlightToolsRepository.swift index 47286a1542..16f4417bf0 100644 --- a/godtools/App/Features/SpotlightTools/Data-DomainInterface/GetSpotlightToolsRepository.swift +++ b/godtools/App/Features/SpotlightTools/Data-DomainInterface/GetSpotlightToolsRepository.swift @@ -46,7 +46,7 @@ class GetSpotlightToolsRepository: GetSpotlightToolsRepositoryInterface { ) .flatMap({ (resourcesChanged: Void, interfaceStrings: ToolListItemInterfaceStringsDomainModel) -> AnyPublisher<[SpotlightToolListItemDomainModel], Never> in - let spotlightToolResources: [ResourceModel] = self.resourcesRepository.getSpotlightTools() + let spotlightToolResources: [ResourceModel] = self.resourcesRepository.getSpotlightTools(sortByDefaultOrder: true) let spotlightTools: [SpotlightToolListItemDomainModel] = spotlightToolResources .map({ diff --git a/godtools/App/Features/ToolDetails/Data-DomainInterface/GetToolDetailsInterfaceStringsRepository.swift b/godtools/App/Features/ToolDetails/Data-DomainInterface/GetToolDetailsInterfaceStringsRepository.swift index 2da5d25057..02326f5f9e 100644 --- a/godtools/App/Features/ToolDetails/Data-DomainInterface/GetToolDetailsInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolDetails/Data-DomainInterface/GetToolDetailsInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolDetailsInterfaceStringsRepository: GetToolDetailsInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolDetails/Data-DomainInterface/GetToolDetailsRepository.swift b/godtools/App/Features/ToolDetails/Data-DomainInterface/GetToolDetailsRepository.swift index ddbd704eec..055ee7cff6 100644 --- a/godtools/App/Features/ToolDetails/Data-DomainInterface/GetToolDetailsRepository.swift +++ b/godtools/App/Features/ToolDetails/Data-DomainInterface/GetToolDetailsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolDetailsRepository: GetToolDetailsRepositoryInterface { @@ -15,16 +16,16 @@ class GetToolDetailsRepository: GetToolDetailsRepositoryInterface { private let languagesRepository: LanguagesRepository private let translationsRepository: TranslationsRepository private let localizationServices: LocalizationServices - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName private let favoritedResourcesRepository: FavoritedResourcesRepository - init(resourcesRepository: ResourcesRepository, languagesRepository: LanguagesRepository, translationsRepository: TranslationsRepository, localizationServices: LocalizationServices, translatedLanguageNameRepository: TranslatedLanguageNameRepository, favoritedResourcesRepository: FavoritedResourcesRepository) { + init(resourcesRepository: ResourcesRepository, languagesRepository: LanguagesRepository, translationsRepository: TranslationsRepository, localizationServices: LocalizationServices, getTranslatedLanguageName: GetTranslatedLanguageName, favoritedResourcesRepository: FavoritedResourcesRepository) { self.resourcesRepository = resourcesRepository self.languagesRepository = languagesRepository self.translationsRepository = translationsRepository self.localizationServices = localizationServices - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName self.favoritedResourcesRepository = favoritedResourcesRepository } @@ -47,8 +48,17 @@ class GetToolDetailsRepository: GetToolDetailsRepositoryInterface { return Just(noToolDomainModel) .eraseToAnyPublisher() } - - guard let translation = translationsRepository.getLatestTranslation(resourceId: toolId, languageCode: translateInLanguage) else { + + let translation: TranslationModel + if let appLanguagetranslation = translationsRepository.getLatestTranslation(resourceId: toolId, languageCode: translateInLanguage) { + + translation = appLanguagetranslation + } + else if let defaultTranslation = translationsRepository.getLatestTranslation(resourceId: toolId, languageCode: toolDataModel.attrDefaultLocale) { + + translation = defaultTranslation + } + else { return Just(noToolDomainModel) .eraseToAnyPublisher() } @@ -62,7 +72,7 @@ class GetToolDetailsRepository: GetToolDetailsRepositoryInterface { let languagesDataModels: [LanguageModel] = languagesRepository.getLanguages(ids: toolDataModel.languageIds) let languageNamesTranslatedInToolLanguage: [String] = languagesDataModels.map { (languageDataModel: LanguageModel) in - self.translatedLanguageNameRepository.getLanguageName(language: languageDataModel.code, translatedInLanguage: translateInLanguage) + self.getTranslatedLanguageName.getLanguageName(language: languageDataModel, translatedInLanguage: translateInLanguage) } let languagesAvailable: String = languageNamesTranslatedInToolLanguage.map({$0}).sorted(by: { $0 < $1 }).joined(separator: ", ") @@ -95,12 +105,12 @@ class GetToolDetailsRepository: GetToolDetailsRepositoryInterface { resourceVariants = [] } - let toolPrimaryLanguageName: String = translatedLanguageNameRepository.getLanguageName(language: toolPrimaryLanguage, translatedInLanguage: translateInLanguage) + let toolPrimaryLanguageName: String = getTranslatedLanguageName.getLanguageName(language: toolPrimaryLanguage, translatedInLanguage: translateInLanguage) let toolParallelLanguageName: String? if let toolParallelLanguage = toolParallelLanguage { - toolParallelLanguageName = translatedLanguageNameRepository.getLanguageName(language: toolParallelLanguage, translatedInLanguage: translateInLanguage) + toolParallelLanguageName = getTranslatedLanguageName.getLanguageName(language: toolParallelLanguage, translatedInLanguage: translateInLanguage) } else { toolParallelLanguageName = nil @@ -126,10 +136,10 @@ class GetToolDetailsRepository: GetToolDetailsRepositoryInterface { name = appLanguageTranslation.translatedName description = appLanguageTranslation.translatedTagline } - else if let englishTranslation = translationsRepository.getLatestTranslation(resourceId: resourceVariant.id, languageCode: LanguageCodeDomainModel.english.rawValue) { + else if let defaultTranslation = translationsRepository.getLatestTranslation(resourceId: resourceVariant.id, languageCode: resourceVariant.attrDefaultLocale) { - name = englishTranslation.translatedName - description = englishTranslation.translatedTagline + name = defaultTranslation.translatedName + description = defaultTranslation.translatedTagline } else { diff --git a/godtools/App/Features/ToolDetails/DependencyContainer/ToolDetailsFeatureDataLayerDependencies.swift b/godtools/App/Features/ToolDetails/DependencyContainer/ToolDetailsFeatureDataLayerDependencies.swift index 99aa1427b8..bd9d3bbfcc 100644 --- a/godtools/App/Features/ToolDetails/DependencyContainer/ToolDetailsFeatureDataLayerDependencies.swift +++ b/godtools/App/Features/ToolDetails/DependencyContainer/ToolDetailsFeatureDataLayerDependencies.swift @@ -46,7 +46,7 @@ class ToolDetailsFeatureDataLayerDependencies { languagesRepository: coreDataLayer.getLanguagesRepository(), translationsRepository: coreDataLayer.getTranslationsRepository(), localizationServices: coreDataLayer.getLocalizationServices(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository(), + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName(), favoritedResourcesRepository: coreDataLayer.getFavoritedResourcesRepository() ) } diff --git a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetCreatingToolScreenShareSessionInterfaceStringsRepository.swift b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetCreatingToolScreenShareSessionInterfaceStringsRepository.swift index 7a4e40ac5d..a7b2d96852 100644 --- a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetCreatingToolScreenShareSessionInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetCreatingToolScreenShareSessionInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetCreatingToolScreenShareSessionInterfaceStringsRepository: GetCreatingToolScreenShareSessionInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetCreatingToolScreenShareSessionTimedOutInterfaceStringsRepository.swift b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetCreatingToolScreenShareSessionTimedOutInterfaceStringsRepository.swift index 78bf0d4c6d..e5e0d9cc2e 100644 --- a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetCreatingToolScreenShareSessionTimedOutInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetCreatingToolScreenShareSessionTimedOutInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetCreatingToolScreenShareSessionTimedOutInterfaceStringsRepository: GetCreatingToolScreenShareSessionTimedOutInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetShareToolScreenShareSessionInterfaceStringsRepository.swift b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetShareToolScreenShareSessionInterfaceStringsRepository.swift index f8b6f43c95..30f16c081f 100644 --- a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetShareToolScreenShareSessionInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetShareToolScreenShareSessionInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetShareToolScreenShareSessionInterfaceStringsRepository: GetShareToolScreenShareSessionInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetToolScreenShareTutorialInterfaceStringsRepository.swift b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetToolScreenShareTutorialInterfaceStringsRepository.swift index 382c7d15e7..453ba2c8f2 100644 --- a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetToolScreenShareTutorialInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetToolScreenShareTutorialInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolScreenShareTutorialInterfaceStringsRepository: GetToolScreenShareTutorialInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetToolScreenShareTutorialRepository.swift b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetToolScreenShareTutorialRepository.swift index 2b2f2d1f7c..1be78d6382 100644 --- a/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetToolScreenShareTutorialRepository.swift +++ b/godtools/App/Features/ToolScreenShare/Data-DomainInterface/GetToolScreenShareTutorialRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolScreenShareTutorialRepository: GetToolScreenShareTutorialRepositoryInterface { diff --git a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetShareToolInterfaceStringsRepository.swift b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetShareToolInterfaceStringsRepository.swift index d2016e3964..364f4e76ca 100644 --- a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetShareToolInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetShareToolInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetShareToolInterfaceStringsRepository: GetShareToolInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsInterfaceStringsRepository.swift b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsInterfaceStringsRepository.swift index 99a5ee7ab8..81585f3a26 100644 --- a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolSettingsInterfaceStringsRepository: GetToolSettingsInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsParallelLanguageRepository.swift b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsParallelLanguageRepository.swift index 1d2fb11b43..b4b35cf22a 100644 --- a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsParallelLanguageRepository.swift +++ b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsParallelLanguageRepository.swift @@ -12,12 +12,12 @@ import Combine class GetToolSettingsParallelLanguageRepository: GetToolSettingsParallelLanguageRepositoryInterface { private let languagesRepository: LanguagesRepository - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName - init(languagesRepository: LanguagesRepository, translatedLanguageNameRepository: TranslatedLanguageNameRepository) { + init(languagesRepository: LanguagesRepository, getTranslatedLanguageName: GetTranslatedLanguageName) { self.languagesRepository = languagesRepository - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName } func getLanguagePublisher(parallelLanguageId: String?, translateInLanguage: AppLanguageDomainModel) -> AnyPublisher { @@ -27,7 +27,7 @@ class GetToolSettingsParallelLanguageRepository: GetToolSettingsParallelLanguage .eraseToAnyPublisher() } - let languageName: String = translatedLanguageNameRepository.getLanguageName( + let languageName: String = getTranslatedLanguageName.getLanguageName( language: language, translatedInLanguage: translateInLanguage ) diff --git a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsPrimaryLanguageRepository.swift b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsPrimaryLanguageRepository.swift index 385896fac8..a9c1dfea71 100644 --- a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsPrimaryLanguageRepository.swift +++ b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsPrimaryLanguageRepository.swift @@ -12,12 +12,12 @@ import Combine class GetToolSettingsPrimaryLanguageRepository: GetToolSettingsPrimaryLanguageRepositoryInterface { private let languagesRepository: LanguagesRepository - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName - init(languagesRepository: LanguagesRepository, translatedLanguageNameRepository: TranslatedLanguageNameRepository) { + init(languagesRepository: LanguagesRepository, getTranslatedLanguageName: GetTranslatedLanguageName) { self.languagesRepository = languagesRepository - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName } func getLanguagePublisher(primaryLanguageId: String, translateInLanguage: AppLanguageDomainModel) -> AnyPublisher { @@ -27,7 +27,7 @@ class GetToolSettingsPrimaryLanguageRepository: GetToolSettingsPrimaryLanguageRe .eraseToAnyPublisher() } - let languageName: String = translatedLanguageNameRepository.getLanguageName( + let languageName: String = getTranslatedLanguageName.getLanguageName( language: language, translatedInLanguage: translateInLanguage ) diff --git a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsToolLanguagesListInterfaceStringsRepository.swift b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsToolLanguagesListInterfaceStringsRepository.swift index 15fa31c4e8..37f245f38f 100644 --- a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsToolLanguagesListInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsToolLanguagesListInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolSettingsToolLanguagesListInterfaceStringsRepository: GetToolSettingsToolLanguagesListInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsToolLanguagesListRepository.swift b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsToolLanguagesListRepository.swift index 4e10c845fe..9cd51a7ff8 100644 --- a/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsToolLanguagesListRepository.swift +++ b/godtools/App/Features/ToolSettings/Data-DomainInterface/GetToolSettingsToolLanguagesListRepository.swift @@ -13,13 +13,13 @@ class GetToolSettingsToolLanguagesListRepository: GetToolSettingsToolLanguagesLi private let resourcesRepository: ResourcesRepository private let languagesRepository: LanguagesRepository - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName - init(resourcesRepository: ResourcesRepository, languagesRepository: LanguagesRepository, translatedLanguageNameRepository: TranslatedLanguageNameRepository) { + init(resourcesRepository: ResourcesRepository, languagesRepository: LanguagesRepository, getTranslatedLanguageName: GetTranslatedLanguageName) { self.resourcesRepository = resourcesRepository self.languagesRepository = languagesRepository - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName } func getToolLanguagesPublisher(listType: ToolSettingsToolLanguagesListTypeDomainModel, primaryLanguageId: String, parallelLanguageId: String?, toolId: String, translateInLanguage: AppLanguageDomainModel) -> AnyPublisher<[ToolSettingsToolLanguageDomainModel], Never> { @@ -51,7 +51,7 @@ class GetToolSettingsToolLanguagesListRepository: GetToolSettingsToolLanguagesLi .getLanguages(ids: languageIds) .map { (language: LanguageModel) in - let languageName: String = translatedLanguageNameRepository.getLanguageName( + let languageName: String = getTranslatedLanguageName.getLanguageName( language: language, translatedInLanguage: translateInLanguage ) diff --git a/godtools/App/Features/ToolSettings/DependencyContainer/ToolSettingsDataLayerDependencies.swift b/godtools/App/Features/ToolSettings/DependencyContainer/ToolSettingsDataLayerDependencies.swift index 6a8f627654..f91a711fec 100644 --- a/godtools/App/Features/ToolSettings/DependencyContainer/ToolSettingsDataLayerDependencies.swift +++ b/godtools/App/Features/ToolSettings/DependencyContainer/ToolSettingsDataLayerDependencies.swift @@ -19,8 +19,22 @@ class ToolSettingsDataLayerDependencies { // MARK: - Data Layer Classes + func getUserToolSettingsRepository() -> UserToolSettingsRepository { + return UserToolSettingsRepository(cache: getRealmUserToolSettingsCache()) + } + + func getRealmUserToolSettingsCache() -> RealmUserToolSettingsCache { + return RealmUserToolSettingsCache(realmDatabase: coreDataLayer.getSharedRealmDatabase()) + } + // MARK: - Domain Interface + func getPersistUserToolLanguageSettingsRepositoryInterface() -> PersistUserToolLanguageSettingsRepositoryInterface { + return PersistUserToolLanguageSettingsRepository( + userToolSettingsRepository: getUserToolSettingsRepository() + ) + } + func getShareToolInterfaceStringsRepositoryInterface() -> GetShareToolInterfaceStringsRepositoryInterface { return GetShareToolInterfaceStringsRepository( resourcesRepository: coreDataLayer.getResourcesRepository(), @@ -38,14 +52,14 @@ class ToolSettingsDataLayerDependencies { func getToolSettingsPrimaryLanguageRepositoryInterface() -> GetToolSettingsPrimaryLanguageRepositoryInterface { return GetToolSettingsPrimaryLanguageRepository( languagesRepository: coreDataLayer.getLanguagesRepository(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository() + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName() ) } func getToolSettingsParallelLanguageRepositoryInterface() -> GetToolSettingsParallelLanguageRepositoryInterface { return GetToolSettingsParallelLanguageRepository( languagesRepository: coreDataLayer.getLanguagesRepository(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository() + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName() ) } @@ -65,7 +79,7 @@ class ToolSettingsDataLayerDependencies { return GetToolSettingsToolLanguagesListRepository( resourcesRepository: coreDataLayer.getResourcesRepository(), languagesRepository: coreDataLayer.getLanguagesRepository(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository() + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName() ) } } diff --git a/godtools/App/Features/ToolSettings/DependencyContainer/ToolSettingsDomainLayerDependencies.swift b/godtools/App/Features/ToolSettings/DependencyContainer/ToolSettingsDomainLayerDependencies.swift index 1876a1e4e0..9a3a8edffc 100644 --- a/godtools/App/Features/ToolSettings/DependencyContainer/ToolSettingsDomainLayerDependencies.swift +++ b/godtools/App/Features/ToolSettings/DependencyContainer/ToolSettingsDomainLayerDependencies.swift @@ -17,6 +17,12 @@ class ToolSettingsDomainLayerDependencies { self.dataLayer = dataLayer } + func getPersistUserToolLanguageSettingsUseCase() -> PersistUserToolLanguageSettingsUseCase { + return PersistUserToolLanguageSettingsUseCase( + persistUserToolLanguageSettingsRepositoryInterface: dataLayer.getPersistUserToolLanguageSettingsRepositoryInterface() + ) + } + func getViewShareToolUseCase() -> ViewShareToolUseCase { return ViewShareToolUseCase( getInterfaceStringsRepository: dataLayer.getShareToolInterfaceStringsRepositoryInterface() diff --git a/godtools/App/Features/ToolSettings/Domain/Entities/UserToolSettingsDomainModel.swift b/godtools/App/Features/ToolSettings/Domain/Entities/UserToolSettingsDomainModel.swift new file mode 100644 index 0000000000..9f63021e1b --- /dev/null +++ b/godtools/App/Features/ToolSettings/Domain/Entities/UserToolSettingsDomainModel.swift @@ -0,0 +1,17 @@ +// +// UserToolSettingsDomainModel.swift +// godtools +// +// Created by Rachael Skeath on 6/3/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +struct UserToolSettingsDomainModel { + + let toolId: String + let primaryLanguageId: String + let parallelLanguageId: String? + let selectedLanguageId: String +} diff --git a/godtools/App/Features/ToolTraining/Presentation/ToolTrainingViewModel.swift b/godtools/App/Features/ToolTraining/Presentation/ToolTrainingViewModel.swift index 3b81cd1c3a..3cf9684669 100644 --- a/godtools/App/Features/ToolTraining/Presentation/ToolTrainingViewModel.swift +++ b/godtools/App/Features/ToolTraining/Presentation/ToolTrainingViewModel.swift @@ -9,6 +9,7 @@ import UIKit import GodToolsToolParser import Combine +import LocalizationServices class ToolTrainingViewModel: NSObject { diff --git a/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterCategoriesInterfaceStringsRepository.swift b/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterCategoriesInterfaceStringsRepository.swift index f86a652fc2..a67ba1d7a0 100644 --- a/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterCategoriesInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterCategoriesInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolFilterCategoriesInterfaceStringsRepository: GetToolFilterCategoriesInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterCategoriesRepository.swift b/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterCategoriesRepository.swift index 6b8bf6d540..d121ad331c 100644 --- a/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterCategoriesRepository.swift +++ b/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterCategoriesRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolFilterCategoriesRepository: GetToolFilterCategoriesRepositoryInterface { @@ -111,8 +112,7 @@ extension GetToolFilterCategoriesRepository { let formatString = localizationServices.stringForLocaleElseSystemElseEnglish( localeIdentifier: translatedInAppLanguage.localeId, - key: ToolStringKeys.ToolFilter.toolsAvailableText.rawValue, - fileType: .stringsdict + key: ToolStringKeys.ToolFilter.toolsAvailableText.rawValue ) return String(format: formatString, locale: Locale(identifier: translatedInAppLanguage), toolsAvailableCount) diff --git a/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterLanguagesInterfaceStringsRepository.swift b/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterLanguagesInterfaceStringsRepository.swift index 9902c88563..ca1c4137a7 100644 --- a/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterLanguagesInterfaceStringsRepository.swift +++ b/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterLanguagesInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolFilterLanguagesInterfaceStringsRepository: GetToolFilterLanguagesInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterLanguagesRepository.swift b/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterLanguagesRepository.swift index 83bf3e479d..91b287ec34 100644 --- a/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterLanguagesRepository.swift +++ b/godtools/App/Features/ToolsFilter/Data-DomainInterface/GetToolFilterLanguagesRepository.swift @@ -8,19 +8,20 @@ import Foundation import Combine +import LocalizationServices class GetToolFilterLanguagesRepository: GetToolFilterLanguagesRepositoryInterface { private let resourcesRepository: ResourcesRepository private let languagesRepository: LanguagesRepository - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName private let localizationServices: LocalizationServices - init(resourcesRepository: ResourcesRepository, languagesRepository: LanguagesRepository, translatedLanguageNameRepository: TranslatedLanguageNameRepository, localizationServices: LocalizationServices) { + init(resourcesRepository: ResourcesRepository, languagesRepository: LanguagesRepository, getTranslatedLanguageName: GetTranslatedLanguageName, localizationServices: LocalizationServices) { self.resourcesRepository = resourcesRepository self.languagesRepository = languagesRepository - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName self.localizationServices = localizationServices } @@ -109,8 +110,8 @@ extension GetToolFilterLanguagesRepository { let toolsAvailableCount: Int = getToolsAvailableCount(for: languageModel.id, filteredByCategoryId: filteredByCategoryId) - let languageName = translatedLanguageNameRepository.getLanguageName(language: languageModel.code, translatedInLanguage: languageModel.code) - let translatedLanguageName = translatedLanguageNameRepository.getLanguageName(language: languageModel.code, translatedInLanguage: translatedInAppLanguage) + let languageName = getTranslatedLanguageName.getLanguageName(language: languageModel, translatedInLanguage: languageModel.code) + let translatedLanguageName = getTranslatedLanguageName.getLanguageName(language: languageModel, translatedInLanguage: translatedInAppLanguage) let languageDomainModel = LanguageDomainModel( analyticsContentLanguage: languageModel.code, @@ -151,8 +152,7 @@ extension GetToolFilterLanguagesRepository { let formatString = localizationServices.stringForLocaleElseSystemElseEnglish( localeIdentifier: translatedInAppLanguage.localeId, - key: ToolStringKeys.ToolFilter.toolsAvailableText.rawValue, - fileType: .stringsdict + key: ToolStringKeys.ToolFilter.toolsAvailableText.rawValue ) let localizedString = String(format: formatString, locale: Locale(identifier: translatedInAppLanguage), toolsAvailableCount) diff --git a/godtools/App/Features/ToolsFilter/DependencyContainer/ToolsFilterFeatureDataLayerDependencies.swift b/godtools/App/Features/ToolsFilter/DependencyContainer/ToolsFilterFeatureDataLayerDependencies.swift index ae030e2d76..7078d6d8e2 100644 --- a/godtools/App/Features/ToolsFilter/DependencyContainer/ToolsFilterFeatureDataLayerDependencies.swift +++ b/godtools/App/Features/ToolsFilter/DependencyContainer/ToolsFilterFeatureDataLayerDependencies.swift @@ -44,7 +44,7 @@ class ToolsFilterFeatureDataLayerDependencies { return GetToolFilterLanguagesRepository( resourcesRepository: coreDataLayer.getResourcesRepository(), languagesRepository: coreDataLayer.getLanguagesRepository(), - translatedLanguageNameRepository: coreDataLayer.getTranslatedLanguageNameRepository(), + getTranslatedLanguageName: coreDataLayer.getTranslatedLanguageName(), localizationServices: coreDataLayer.getLocalizationServices() ) } diff --git a/godtools/App/Features/Tutorial/Data-DomainInterface/GetTutorialInterfaceStringsRepository.swift b/godtools/App/Features/Tutorial/Data-DomainInterface/GetTutorialInterfaceStringsRepository.swift index 4b2ec5dd4a..475dbde7a5 100644 --- a/godtools/App/Features/Tutorial/Data-DomainInterface/GetTutorialInterfaceStringsRepository.swift +++ b/godtools/App/Features/Tutorial/Data-DomainInterface/GetTutorialInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetTutorialInterfaceStringsRepository: GetTutorialInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Features/Tutorial/Data-DomainInterface/GetTutorialRepository.swift b/godtools/App/Features/Tutorial/Data-DomainInterface/GetTutorialRepository.swift index a877b96fd4..23003ddb3e 100644 --- a/godtools/App/Features/Tutorial/Data-DomainInterface/GetTutorialRepository.swift +++ b/godtools/App/Features/Tutorial/Data-DomainInterface/GetTutorialRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetTutorialRepository: GetTutorialRepositoryInterface { diff --git a/godtools/App/Flows/App/AppFlow.swift b/godtools/App/Flows/App/AppFlow.swift index 18c2c5cbd7..fcf9d5f7db 100644 --- a/godtools/App/Flows/App/AppFlow.swift +++ b/godtools/App/Flows/App/AppFlow.swift @@ -21,6 +21,7 @@ class AppFlow: NSObject, ToolNavigationFlow, Flow { private let resourceViewsService: ResourceViewsService private let deepLinkingService: DeepLinkingService private let inAppMessaging: FirebaseInAppMessaging + private let dashboardTabObserver = CurrentValueSubject(AppFlow.defaultStartingDashboardTab) private var onboardingFlow: OnboardingFlow? private var menuFlow: MenuFlow? @@ -192,7 +193,14 @@ class AppFlow: NSObject, ToolNavigationFlow, Flow { navigationController.pushViewController(toolDetails, animated: true) case .openToolTappedFromToolDetails(let toolId, let primaryLanguage, let parallelLanguage, let selectedLanguageIndex): - navigateToTool(toolDataModelId: toolId, primaryLanguage: primaryLanguage, parallelLanguage: parallelLanguage, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: false) + + if dashboardTabObserver.value == .favorites { + + navigateToToolWithUserToolLanguageSettingsApplied(toolDataModelId: toolId, trainingTipsEnabled: false) + } else { + + navigateToTool(toolDataModelId: toolId, primaryLanguage: primaryLanguage, parallelLanguage: parallelLanguage, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: false) + } case .lessonTappedFromLessonsList(let lessonListItem): navigateToToolInAppLanguage(toolDataModelId: lessonListItem.dataModelId, trainingTipsEnabled: false) @@ -208,16 +216,17 @@ class AppFlow: NSObject, ToolNavigationFlow, Flow { let toolDetails = getToolDetails( toolId: tool.dataModelId, parallelLanguage: nil, - selectedLanguageIndex: nil + selectedLanguageIndex: nil, + shouldPersistToolSettings: true ) navigationController.pushViewController(toolDetails, animated: true) case .openToolTappedFromFavorites(let tool): - navigateToToolInAppLanguage(toolDataModelId: tool.dataModelId, trainingTipsEnabled: false) + navigateToToolWithUserToolLanguageSettingsApplied(toolDataModelId: tool.dataModelId, trainingTipsEnabled: false) case .toolTappedFromFavorites(let tool): - navigateToToolInAppLanguage(toolDataModelId: tool.dataModelId, trainingTipsEnabled: false) + navigateToToolWithUserToolLanguageSettingsApplied(toolDataModelId: tool.dataModelId, trainingTipsEnabled: false) case .unfavoriteToolTappedFromFavorites(let tool): @@ -238,17 +247,18 @@ class AppFlow: NSObject, ToolNavigationFlow, Flow { let toolDetails = getToolDetails( toolId: tool.dataModelId, parallelLanguage: nil, - selectedLanguageIndex: nil + selectedLanguageIndex: nil, + shouldPersistToolSettings: true ) navigationController.pushViewController(toolDetails, animated: true) case .openToolTappedFromAllYourFavoriteTools(let tool): - navigateToToolInAppLanguage(toolDataModelId: tool.dataModelId, trainingTipsEnabled: false) + navigateToToolWithUserToolLanguageSettingsApplied(toolDataModelId: tool.dataModelId, trainingTipsEnabled: false) case .toolTappedFromAllYourFavoritedTools(let tool): - navigateToToolInAppLanguage(toolDataModelId: tool.dataModelId, trainingTipsEnabled: false) - + navigateToToolWithUserToolLanguageSettingsApplied(toolDataModelId: tool.dataModelId, trainingTipsEnabled: false) + case .unfavoriteToolTappedFromAllYourFavoritedTools(let tool, let didConfirmToolRemovalSubject): presentConfirmRemoveToolFromFavoritesAlertView( @@ -510,7 +520,8 @@ extension AppFlow { flowDelegate: self ), getCurrentAppLanguageUseCase: appDiContainer.feature.appLanguage.domainLayer.getCurrentAppLanguageUseCase(), - viewDashboardUseCase: appDiContainer.feature.dashboard.domainLayer.getViewDashboardUseCase() + viewDashboardUseCase: appDiContainer.feature.dashboard.domainLayer.getViewDashboardUseCase(), + dashboardTabObserver: dashboardTabObserver ) let view = DashboardView(viewModel: viewModel) @@ -700,10 +711,14 @@ extension AppFlow { case .onboarding(let appLanguage): - let userAppLanguageCache: RealmUserAppLanguageCache = appDiContainer.feature.appLanguage.dataLayer.getUserAppLanguageCache() - - userAppLanguageCache.storeLanguage(languageId: appLanguage) + let userAppLanguageRepository: UserAppLanguageRepository = appDiContainer.feature.appLanguage.dataLayer.getUserAppLanguageRepository() + userAppLanguageRepository.storeLanguagePublisher(languageId: appLanguage) + .sink { _ in + + } + .store(in: &cancellables) + navigateToOnboarding(animated: true) } } @@ -713,7 +728,7 @@ extension AppFlow { extension AppFlow { - private func navigateToToolInAppLanguage(toolDataModelId: String, trainingTipsEnabled: Bool) { + private func navigateToToolInAppLanguage(toolDataModelId: String, trainingTipsEnabled: Bool, shouldPersistToolSettings: Bool = false) { let languagesRepository: LanguagesRepository = appDiContainer.dataLayer.getLanguagesRepository() @@ -726,10 +741,46 @@ extension AppFlow { languageIds = Array() } - navigateToTool(toolDataModelId: toolDataModelId, languageIds: languageIds, selectedLanguageIndex: nil, trainingTipsEnabled: trainingTipsEnabled) + navigateToTool(toolDataModelId: toolDataModelId, languageIds: languageIds, selectedLanguageIndex: nil, trainingTipsEnabled: trainingTipsEnabled, shouldPersistToolSettings: shouldPersistToolSettings) + } + + private func navigateToToolWithUserToolLanguageSettingsApplied(toolDataModelId: String, trainingTipsEnabled: Bool) { + + let userToolSettingsRepository: UserToolSettingsRepository = appDiContainer.feature.toolSettings.dataLayer.getUserToolSettingsRepository() + + if let userToolSettings = userToolSettingsRepository.getUserToolSettings(toolId: toolDataModelId) { + + let selectedLanguageIndex: Int = userToolSettings.selectedLanguageId == userToolSettings.primaryLanguageId ? 0 : 1 + + navigateToTool( + toolDataModelId: toolDataModelId, + primaryLanguageId: userToolSettings.primaryLanguageId, + parallelLanguageId: userToolSettings.parallelLanguageId, + selectedLanguageIndex: selectedLanguageIndex, + trainingTipsEnabled: trainingTipsEnabled, + shouldPersistToolSettings: true + ) + + } else { + + navigateToToolInAppLanguage(toolDataModelId: toolDataModelId, trainingTipsEnabled: trainingTipsEnabled, shouldPersistToolSettings: true) + } + } + + private func navigateToTool(toolDataModelId: String, primaryLanguageId: String, parallelLanguageId: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, shouldPersistToolSettings: Bool = false) { + + let languagesRepository: LanguagesRepository = appDiContainer.dataLayer.getLanguagesRepository() + + var languageIds: [String] = [primaryLanguageId] + + if let parallelLanguageId = parallelLanguageId { + languageIds.append(parallelLanguageId) + } + + navigateToTool(toolDataModelId: toolDataModelId, languageIds: languageIds, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: trainingTipsEnabled, shouldPersistToolSettings: shouldPersistToolSettings) } - private func navigateToTool(toolDataModelId: String, primaryLanguage: AppLanguageDomainModel, parallelLanguage: AppLanguageDomainModel?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool) { + private func navigateToTool(toolDataModelId: String, primaryLanguage: AppLanguageDomainModel, parallelLanguage: AppLanguageDomainModel?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, shouldPersistToolSettings: Bool = false) { let languagesRepository: LanguagesRepository = appDiContainer.dataLayer.getLanguagesRepository() @@ -743,10 +794,10 @@ extension AppFlow { languageIds.append(languageModel.id) } - navigateToTool(toolDataModelId: toolDataModelId, languageIds: languageIds, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: trainingTipsEnabled) + navigateToTool(toolDataModelId: toolDataModelId, languageIds: languageIds, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: trainingTipsEnabled, shouldPersistToolSettings: shouldPersistToolSettings) } - private func navigateToTool(toolDataModelId: String, languageIds: [String], selectedLanguageIndex: Int?, trainingTipsEnabled: Bool) { + private func navigateToTool(toolDataModelId: String, languageIds: [String], selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, shouldPersistToolSettings: Bool = false) { let languagesRepository: LanguagesRepository = appDiContainer.dataLayer.getLanguagesRepository() @@ -768,7 +819,8 @@ extension AppFlow { liveShareStream: nil, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: trainingTipsEnabled, - initialPage: nil + initialPage: nil, + shouldPersistToolSettings: shouldPersistToolSettings ) } } @@ -987,7 +1039,7 @@ extension AppFlow { extension AppFlow { - private func getToolDetails(toolId: String, parallelLanguage: AppLanguageDomainModel?, selectedLanguageIndex: Int?) -> UIViewController { + private func getToolDetails(toolId: String, parallelLanguage: AppLanguageDomainModel?, selectedLanguageIndex: Int?, shouldPersistToolSettings: Bool = false) -> UIViewController { let viewModel = ToolDetailsViewModel( flowDelegate: self, @@ -1002,7 +1054,7 @@ extension AppFlow { toggleToolFavoritedUseCase: appDiContainer.feature.favorites.domainLayer.getToggleFavoritedToolUseCase(), attachmentsRepository: appDiContainer.dataLayer.getAttachmentsRepository(), trackScreenViewAnalyticsUseCase: appDiContainer.domainLayer.getTrackScreenViewAnalyticsUseCase(), - trackActionAnalyticsUseCase: appDiContainer.domainLayer.getTrackActionAnalyticsUseCase() + trackActionAnalyticsUseCase: appDiContainer.domainLayer.getTrackActionAnalyticsUseCase() ) let view = ToolDetailsView(viewModel: viewModel) diff --git a/godtools/App/Flows/ArticleDeepLink/ArticleDeepLinkFlow.swift b/godtools/App/Flows/ArticleDeepLink/ArticleDeepLinkFlow.swift index ff3298370a..3a6f67fe11 100644 --- a/godtools/App/Flows/ArticleDeepLink/ArticleDeepLinkFlow.swift +++ b/godtools/App/Flows/ArticleDeepLink/ArticleDeepLinkFlow.swift @@ -8,6 +8,7 @@ import UIKit import Combine +import LocalizationServices class ArticleDeepLinkFlow: Flow { diff --git a/godtools/App/Flows/ChooseYourOwnAdventure/ChooseYourOwnAdventureFlow.swift b/godtools/App/Flows/ChooseYourOwnAdventure/ChooseYourOwnAdventureFlow.swift index 7c955152a5..bf15969274 100644 --- a/godtools/App/Flows/ChooseYourOwnAdventure/ChooseYourOwnAdventureFlow.swift +++ b/godtools/App/Flows/ChooseYourOwnAdventure/ChooseYourOwnAdventureFlow.swift @@ -87,7 +87,7 @@ extension ChooseYourOwnAdventureFlow { translationsRepository: appDiContainer.dataLayer.getTranslationsRepository(), mobileContentEventAnalytics: appDiContainer.getMobileContentRendererEventAnalyticsTracking(), getCurrentAppLanguageUseCase: appDiContainer.feature.appLanguage.domainLayer.getCurrentAppLanguageUseCase(), - translatedLanguageNameRepository: appDiContainer.dataLayer.getTranslatedLanguageNameRepository(), + getTranslatedLanguageName: appDiContainer.dataLayer.getTranslatedLanguageName(), trainingTipsEnabled: false, incrementUserCounterUseCase: appDiContainer.domainLayer.getIncrementUserCounterUseCase(), selectedLanguageIndex: selectedLanguageIndex diff --git a/godtools/App/Flows/Flow+PresentAlert/Flow+PresentAlert.swift b/godtools/App/Flows/Flow+PresentAlert/Flow+PresentAlert.swift index 40675f04c2..7bed7bc6c6 100644 --- a/godtools/App/Flows/Flow+PresentAlert/Flow+PresentAlert.swift +++ b/godtools/App/Flows/Flow+PresentAlert/Flow+PresentAlert.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices extension Flow { diff --git a/godtools/App/Flows/Flow+PresentError/Flow+PresentError.swift b/godtools/App/Flows/Flow+PresentError/Flow+PresentError.swift index 7d00dec2d3..ffd82bea87 100644 --- a/godtools/App/Flows/Flow+PresentError/Flow+PresentError.swift +++ b/godtools/App/Flows/Flow+PresentError/Flow+PresentError.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices extension Flow { diff --git a/godtools/App/Flows/Flow+PresentNativeMailApp/Flow+PresentNativeMailApp.swift b/godtools/App/Flows/Flow+PresentNativeMailApp/Flow+PresentNativeMailApp.swift index 8ac59d7e2e..5d29c2e6c6 100644 --- a/godtools/App/Flows/Flow+PresentNativeMailApp/Flow+PresentNativeMailApp.swift +++ b/godtools/App/Flows/Flow+PresentNativeMailApp/Flow+PresentNativeMailApp.swift @@ -8,6 +8,7 @@ import Foundation import MessageUI +import LocalizationServices extension Flow { diff --git a/godtools/App/Flows/Lesson/LessonFlow.swift b/godtools/App/Flows/Lesson/LessonFlow.swift index ebf2719c25..4bf3bdc4c9 100644 --- a/godtools/App/Flows/Lesson/LessonFlow.swift +++ b/godtools/App/Flows/Lesson/LessonFlow.swift @@ -54,7 +54,7 @@ class LessonFlow: ToolNavigationFlow, Flow { translationsRepository: appDiContainer.dataLayer.getTranslationsRepository(), mobileContentEventAnalytics: appDiContainer.getMobileContentRendererEventAnalyticsTracking(), getCurrentAppLanguageUseCase: appDiContainer.feature.appLanguage.domainLayer.getCurrentAppLanguageUseCase(), - translatedLanguageNameRepository: appDiContainer.dataLayer.getTranslatedLanguageNameRepository(), + getTranslatedLanguageName: appDiContainer.dataLayer.getTranslatedLanguageName(), trainingTipsEnabled: trainingTipsEnabled, incrementUserCounterUseCase: appDiContainer.domainLayer.getIncrementUserCounterUseCase() ) diff --git a/godtools/App/Flows/Menu/MenuFlow.swift b/godtools/App/Flows/Menu/MenuFlow.swift index 1b45e6cba8..704b8d302e 100644 --- a/godtools/App/Flows/Menu/MenuFlow.swift +++ b/godtools/App/Flows/Menu/MenuFlow.swift @@ -10,6 +10,7 @@ import UIKit import MessageUI import SwiftUI import Combine +import LocalizationServices class MenuFlow: Flow { diff --git a/godtools/App/Flows/ToolNavigation/ToolNavigationFlow.swift b/godtools/App/Flows/ToolNavigation/ToolNavigationFlow.swift index e73be10497..f868bd767b 100644 --- a/godtools/App/Flows/ToolNavigation/ToolNavigationFlow.swift +++ b/godtools/App/Flows/ToolNavigation/ToolNavigationFlow.swift @@ -36,11 +36,12 @@ extension ToolNavigationFlow { liveShareStream: toolDeepLink.liveShareStream, selectedLanguageIndex: toolDeepLink.selectedLanguageIndex, trainingTipsEnabled: false, - initialPage: toolDeepLink.mobileContentPage + initialPage: toolDeepLink.mobileContentPage, + shouldPersistToolSettings: false ) } - func navigateToTool(appLanguage: AppLanguageDomainModel, resourceId: String, languageIds: [String], liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?) { + func navigateToTool(appLanguage: AppLanguageDomainModel, resourceId: String, languageIds: [String], liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?, shouldPersistToolSettings: Bool) { let determineToolTranslationsToDownload = DetermineToolTranslationsToDownload( resourceId: resourceId, @@ -55,11 +56,12 @@ extension ToolNavigationFlow { liveShareStream: liveShareStream, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: trainingTipsEnabled, - initialPage: initialPage + initialPage: initialPage, + shouldPersistToolSettings: shouldPersistToolSettings ) } - private func navigateToToolAndDetermineToolTranslationsToDownload(appLanguage: AppLanguageDomainModel, determineToolTranslationsToDownload: DetermineToolTranslationsToDownloadType, liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?) { + private func navigateToToolAndDetermineToolTranslationsToDownload(appLanguage: AppLanguageDomainModel, determineToolTranslationsToDownload: DetermineToolTranslationsToDownloadType, liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?, shouldPersistToolSettings: Bool) { let didDownloadToolTranslationsClosure = { [weak self] (result: Result) in @@ -73,7 +75,8 @@ extension ToolNavigationFlow { liveShareStream: liveShareStream, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: trainingTipsEnabled, - initialPage: initialPage + initialPage: initialPage, + shouldPersistToolSettings: shouldPersistToolSettings ) case .failure(let responseError): @@ -93,7 +96,7 @@ extension ToolNavigationFlow { self.downloadToolTranslationFlow = downloadToolTranslationFlow } - private func navigateToTool(appLanguage: AppLanguageDomainModel, toolTranslations: ToolTranslationsDomainModel, liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?) { + private func navigateToTool(appLanguage: AppLanguageDomainModel, toolTranslations: ToolTranslationsDomainModel, liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?, shouldPersistToolSettings: Bool) { let resourceType: ResourceType = toolTranslations.tool.resourceTypeEnum @@ -131,7 +134,8 @@ extension ToolNavigationFlow { liveShareStream: liveShareStream, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: trainingTipsEnabled, - initialPage: initialPage + initialPage: initialPage, + shouldPersistToolSettings: shouldPersistToolSettings ) case .chooseYourOwnAdventure: diff --git a/godtools/App/Flows/Tract/TractFlow.swift b/godtools/App/Flows/Tract/TractFlow.swift index b1f6bae70f..6a8d8ec773 100644 --- a/godtools/App/Flows/Tract/TractFlow.swift +++ b/godtools/App/Flows/Tract/TractFlow.swift @@ -9,6 +9,7 @@ import UIKit import GodToolsToolParser import Combine +import LocalizationServices class TractFlow: ToolNavigationFlow, Flow { @@ -28,7 +29,7 @@ class TractFlow: ToolNavigationFlow, Flow { var tractFlow: TractFlow? var downloadToolTranslationFlow: DownloadToolTranslationsFlow? - init(flowDelegate: FlowDelegate, appDiContainer: AppDiContainer, sharedNavigationController: AppNavigationController?, appLanguage: AppLanguageDomainModel, toolTranslations: ToolTranslationsDomainModel, liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?) { + init(flowDelegate: FlowDelegate, appDiContainer: AppDiContainer, sharedNavigationController: AppNavigationController?, appLanguage: AppLanguageDomainModel, toolTranslations: ToolTranslationsDomainModel, liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?, shouldPersistToolSettings: Bool) { self.flowDelegate = flowDelegate self.appDiContainer = appDiContainer @@ -40,7 +41,8 @@ class TractFlow: ToolNavigationFlow, Flow { liveShareStream: liveShareStream, selectedLanguageIndex: selectedLanguageIndex, trainingTipsEnabled: trainingTipsEnabled, - initialPage: initialPage + initialPage: initialPage, + shouldPersistToolSettings: shouldPersistToolSettings ) if let sharedNavController = sharedNavigationController { @@ -152,7 +154,7 @@ class TractFlow: ToolNavigationFlow, Flow { extension TractFlow { - private func getToolView(toolTranslations: ToolTranslationsDomainModel, liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?) -> UIViewController { + private func getToolView(toolTranslations: ToolTranslationsDomainModel, liveShareStream: String?, selectedLanguageIndex: Int?, trainingTipsEnabled: Bool, initialPage: MobileContentPagesPage?, shouldPersistToolSettings: Bool) -> UIViewController { let navigation: MobileContentRendererNavigation = appDiContainer.getMobileContentRendererNavigation( parentFlow: self, @@ -182,13 +184,15 @@ extension TractFlow { translationsRepository: appDiContainer.dataLayer.getTranslationsRepository(), mobileContentEventAnalytics: appDiContainer.getMobileContentRendererEventAnalyticsTracking(), getCurrentAppLanguageUseCase: appDiContainer.feature.appLanguage.domainLayer.getCurrentAppLanguageUseCase(), - translatedLanguageNameRepository: appDiContainer.dataLayer.getTranslatedLanguageNameRepository(), + getTranslatedLanguageName: appDiContainer.dataLayer.getTranslatedLanguageName(), toolOpenedAnalytics: appDiContainer.getToolOpenedAnalytics(), liveShareStream: liveShareStream, initialPage: initialPage, trainingTipsEnabled: trainingTipsEnabled, - incrementUserCounterUseCase: appDiContainer.domainLayer.getIncrementUserCounterUseCase(), - selectedLanguageIndex: selectedLanguageIndex + incrementUserCounterUseCase: appDiContainer.domainLayer.getIncrementUserCounterUseCase(), + selectedLanguageIndex: selectedLanguageIndex, + persistUserToolLanguageSettingsUseCase: appDiContainer.feature.toolSettings.domainLayer.getPersistUserToolLanguageSettingsUseCase(), + shouldPersistToolSettings: shouldPersistToolSettings ) navigationController.setSemanticContentAttribute(semanticContentAttribute: navBarLayoutDirection) diff --git a/godtools/App/Services/Renderer/Renderer/MobileContentRendererPageViewFactories.swift b/godtools/App/Services/Renderer/Renderer/MobileContentRendererPageViewFactories.swift index 70becb8dd1..15a4d68a9b 100644 --- a/godtools/App/Services/Renderer/Renderer/MobileContentRendererPageViewFactories.swift +++ b/godtools/App/Services/Renderer/Renderer/MobileContentRendererPageViewFactories.swift @@ -8,6 +8,7 @@ import Foundation import GodToolsToolParser +import LocalizationServices class MobileContentRendererPageViewFactories: MobileContentPageViewFactoryType { diff --git a/godtools/App/Services/Renderer/Views/MobileContent/Views/Error/MobileContentErrorViewModel.swift b/godtools/App/Services/Renderer/Views/MobileContent/Views/Error/MobileContentErrorViewModel.swift index 44449f0c45..1e1530f0eb 100644 --- a/godtools/App/Services/Renderer/Views/MobileContent/Views/Error/MobileContentErrorViewModel.swift +++ b/godtools/App/Services/Renderer/Views/MobileContent/Views/Error/MobileContentErrorViewModel.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices class MobileContentErrorViewModel { diff --git a/godtools/App/Services/Renderer/Views/MobileContent/Views/Pages/MobileContentPagesViewModel.swift b/godtools/App/Services/Renderer/Views/MobileContent/Views/Pages/MobileContentPagesViewModel.swift index 7e46e3b2e5..73c6c4b465 100644 --- a/godtools/App/Services/Renderer/Views/MobileContent/Views/Pages/MobileContentPagesViewModel.swift +++ b/godtools/App/Services/Renderer/Views/MobileContent/Views/Pages/MobileContentPagesViewModel.swift @@ -16,7 +16,7 @@ class MobileContentPagesViewModel: NSObject, ObservableObject { private let translationsRepository: TranslationsRepository private let mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking private let getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName private let initialPageRenderingType: MobileContentPagesInitialPageRenderingType private let initialPage: MobileContentPagesPage private let initialSelectedLanguageIndex: Int @@ -43,7 +43,7 @@ class MobileContentPagesViewModel: NSObject, ObservableObject { let pageNavigationEventSignal: SignalValue = SignalValue() let incrementUserCounterUseCase: IncrementUserCounterUseCase - init(renderer: MobileContentRenderer, initialPage: MobileContentPagesPage?, resourcesRepository: ResourcesRepository, translationsRepository: TranslationsRepository, mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, translatedLanguageNameRepository: TranslatedLanguageNameRepository, initialPageRenderingType: MobileContentPagesInitialPageRenderingType, trainingTipsEnabled: Bool, incrementUserCounterUseCase: IncrementUserCounterUseCase, selectedLanguageIndex: Int?) { + init(renderer: MobileContentRenderer, initialPage: MobileContentPagesPage?, resourcesRepository: ResourcesRepository, translationsRepository: TranslationsRepository, mobileContentEventAnalytics: MobileContentRendererEventAnalyticsTracking, getCurrentAppLanguageUseCase: GetCurrentAppLanguageUseCase, getTranslatedLanguageName: GetTranslatedLanguageName, initialPageRenderingType: MobileContentPagesInitialPageRenderingType, trainingTipsEnabled: Bool, incrementUserCounterUseCase: IncrementUserCounterUseCase, selectedLanguageIndex: Int?) { self.renderer = CurrentValueSubject(renderer) self.currentPageRenderer = CurrentValueSubject(renderer.pageRenderers[0]) @@ -52,7 +52,7 @@ class MobileContentPagesViewModel: NSObject, ObservableObject { self.translationsRepository = translationsRepository self.mobileContentEventAnalytics = mobileContentEventAnalytics self.getCurrentAppLanguageUseCase = getCurrentAppLanguageUseCase - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName self.initialPageRenderingType = initialPageRenderingType self.trainingTipsEnabled = trainingTipsEnabled self.incrementUserCounterUseCase = incrementUserCounterUseCase @@ -74,7 +74,7 @@ class MobileContentPagesViewModel: NSObject, ObservableObject { .sink { [weak self] (languages: [LanguageDomainModel], appLanguage: AppLanguageDomainModel) in self?.languageNames = languages.map({ (language: LanguageDomainModel) in - translatedLanguageNameRepository.getLanguageName(language: language.localeIdentifier, translatedInLanguage: appLanguage) + getTranslatedLanguageName.getLanguageName(language: language.localeIdentifier, translatedInLanguage: appLanguage) }) } .store(in: &cancellables) diff --git a/godtools/App/Services/Renderer/Views/Tool/ViewFactory/ToolPageViewFactory.swift b/godtools/App/Services/Renderer/Views/Tool/ViewFactory/ToolPageViewFactory.swift index 9178b065be..db9775f3ba 100644 --- a/godtools/App/Services/Renderer/Views/Tool/ViewFactory/ToolPageViewFactory.swift +++ b/godtools/App/Services/Renderer/Views/Tool/ViewFactory/ToolPageViewFactory.swift @@ -8,6 +8,7 @@ import Foundation import GodToolsToolParser +import LocalizationServices class ToolPageViewFactory: MobileContentPageViewFactoryType { diff --git a/godtools/App/Services/Renderer/Views/Tool/Views/Card/ToolPageCardViewModel.swift b/godtools/App/Services/Renderer/Views/Tool/Views/Card/ToolPageCardViewModel.swift index 405af92ef4..9bcd454a3c 100644 --- a/godtools/App/Services/Renderer/Views/Tool/Views/Card/ToolPageCardViewModel.swift +++ b/godtools/App/Services/Renderer/Views/Tool/Views/Card/ToolPageCardViewModel.swift @@ -8,6 +8,7 @@ import UIKit import GodToolsToolParser +import LocalizationServices class ToolPageCardViewModel: MobileContentViewModel { diff --git a/godtools/App/Services/Renderer/Views/Tool/Views/Form/ToolPageFormViewModel.swift b/godtools/App/Services/Renderer/Views/Tool/Views/Form/ToolPageFormViewModel.swift index 85ee2e2bd2..99968d16ef 100644 --- a/godtools/App/Services/Renderer/Views/Tool/Views/Form/ToolPageFormViewModel.swift +++ b/godtools/App/Services/Renderer/Views/Tool/Views/Form/ToolPageFormViewModel.swift @@ -8,6 +8,7 @@ import Foundation import GodToolsToolParser +import LocalizationServices class ToolPageFormViewModel: MobileContentFormViewModel { diff --git a/godtools/App/Share/Application/ApplicationLayout/ApplicationLayout.swift b/godtools/App/Share/Application/ApplicationLayout/ApplicationLayout.swift index 8095755d55..0bd05054b2 100644 --- a/godtools/App/Share/Application/ApplicationLayout/ApplicationLayout.swift +++ b/godtools/App/Share/Application/ApplicationLayout/ApplicationLayout.swift @@ -63,16 +63,16 @@ class ApplicationLayout: ObservableObject { } .switchToLatest() .receive(on: DispatchQueue.main) - .sink { (interfaceLayoutDirection: AppInterfaceLayoutDirectionDomainModel) in + .sink { [weak self] (interfaceLayoutDirection: AppInterfaceLayoutDirectionDomainModel) in let newLayoutDirection: ApplicationLayoutDirection = interfaceLayoutDirection == .leftToRight ? .leftToRight : .rightToLeft - if newLayoutDirection != self.currentDirection { + if newLayoutDirection != self?.currentDirection { - self.currentDirection = newLayoutDirection + self?.currentDirection = newLayoutDirection - self.layoutDirection = newLayoutDirection.layoutDirection - self.semanticContentAttributeSubject.send(newLayoutDirection.semanticContentAttribute) + self?.layoutDirection = newLayoutDirection.layoutDirection + self?.semanticContentAttributeSubject.send(newLayoutDirection.semanticContentAttribute) } } .store(in: &cancellables) diff --git a/godtools/App/Share/Common/LocaleLanguageName/LocaleLanguageName.swift b/godtools/App/Share/Common/LocaleLanguageName/LocaleLanguageName.swift index c361e5b801..ffddbb1ed2 100644 --- a/godtools/App/Share/Common/LocaleLanguageName/LocaleLanguageName.swift +++ b/godtools/App/Share/Common/LocaleLanguageName/LocaleLanguageName.swift @@ -8,7 +8,7 @@ import Foundation -class LocaleLanguageName { +class LocaleLanguageName: LocaleLanguageNameInterface { init() { @@ -27,4 +27,18 @@ class LocaleLanguageName { return translateInLocale.localizedString(forLanguageCode: forLanguageCode) } + + func getLanguageName(forLocaleId: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? { + + let translateInLocale: Locale + + if let translatedInLanguageId = translatedInLanguageId, !translatedInLanguageId.isEmpty { + translateInLocale = Locale(identifier: translatedInLanguageId) + } + else { + translateInLocale = Locale(identifier: forLocaleId) + } + + return translateInLocale.localizedString(forLanguageCode: forLocaleId) + } } diff --git a/godtools/App/Share/Common/LocaleLanguageName/LocaleLanguageNameInterface.swift b/godtools/App/Share/Common/LocaleLanguageName/LocaleLanguageNameInterface.swift new file mode 100644 index 0000000000..d6293c024b --- /dev/null +++ b/godtools/App/Share/Common/LocaleLanguageName/LocaleLanguageNameInterface.swift @@ -0,0 +1,15 @@ +// +// LocaleLanguageNameInterface.swift +// godtools +// +// Created by Levi Eggert on 7/1/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +protocol LocaleLanguageNameInterface { + + func getLanguageName(forLanguageCode: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? + func getLanguageName(forLocaleId: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? +} diff --git a/godtools/App/Share/Common/LocaleLanguageRegionName/LocaleLanguageRegionName.swift b/godtools/App/Share/Common/LocaleLanguageRegionName/LocaleLanguageRegionName.swift index 7e9e7ae128..42b6dee42c 100644 --- a/godtools/App/Share/Common/LocaleLanguageRegionName/LocaleLanguageRegionName.swift +++ b/godtools/App/Share/Common/LocaleLanguageRegionName/LocaleLanguageRegionName.swift @@ -8,7 +8,7 @@ import Foundation -class LocaleLanguageRegionName { +class LocaleLanguageRegionName: LocaleLanguageRegionNameInterface { init() { diff --git a/godtools/App/Share/Common/LocaleLanguageRegionName/LocaleLanguageRegionNameInterface.swift b/godtools/App/Share/Common/LocaleLanguageRegionName/LocaleLanguageRegionNameInterface.swift new file mode 100644 index 0000000000..ef74dc3128 --- /dev/null +++ b/godtools/App/Share/Common/LocaleLanguageRegionName/LocaleLanguageRegionNameInterface.swift @@ -0,0 +1,14 @@ +// +// LocaleLanguageRegionNameInterface.swift +// godtools +// +// Created by Levi Eggert on 7/1/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +protocol LocaleLanguageRegionNameInterface { + + func getRegionName(forRegionCode: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? +} diff --git a/godtools/App/Share/Common/LocaleLanguageScriptName/LocaleLanguageScriptName.swift b/godtools/App/Share/Common/LocaleLanguageScriptName/LocaleLanguageScriptName.swift index 461fe08b54..7c2aab691c 100644 --- a/godtools/App/Share/Common/LocaleLanguageScriptName/LocaleLanguageScriptName.swift +++ b/godtools/App/Share/Common/LocaleLanguageScriptName/LocaleLanguageScriptName.swift @@ -8,7 +8,7 @@ import Foundation -class LocaleLanguageScriptName { +class LocaleLanguageScriptName: LocaleLanguageScriptNameInterface { init() { diff --git a/godtools/App/Share/Common/LocaleLanguageScriptName/LocaleLanguageScriptNameInterface.swift b/godtools/App/Share/Common/LocaleLanguageScriptName/LocaleLanguageScriptNameInterface.swift new file mode 100644 index 0000000000..a5e57daf77 --- /dev/null +++ b/godtools/App/Share/Common/LocaleLanguageScriptName/LocaleLanguageScriptNameInterface.swift @@ -0,0 +1,14 @@ +// +// LocaleLanguageScriptNameInterface.swift +// godtools +// +// Created by Levi Eggert on 7/1/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +protocol LocaleLanguageScriptNameInterface { + + func getScriptName(forScriptCode: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? +} diff --git a/godtools/App/Share/Common/SyncInvalidator/SyncInvalidator.swift b/godtools/App/Share/Common/SyncInvalidator/SyncInvalidator.swift new file mode 100644 index 0000000000..444d20dc71 --- /dev/null +++ b/godtools/App/Share/Common/SyncInvalidator/SyncInvalidator.swift @@ -0,0 +1,54 @@ +// +// SyncInvalidator.swift +// godtools +// +// Created by Levi Eggert on 5/7/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +class SyncInvalidator { + + private(set) var timeInterval: SyncInvalidatorTimeInterval + private(set) var lastSync: Date? + + let id: String + + init(id: String, timeInterval: SyncInvalidatorTimeInterval) { + + self.id = id + self.timeInterval = timeInterval + } + + var shouldSync: Bool { + + let shouldSync: Bool + + if let lastSync = lastSync { + + let elapsedTimeInSeconds: TimeInterval = Date().timeIntervalSince(lastSync) + let elapsedTimeInMinutes: TimeInterval = elapsedTimeInSeconds / 60 + + switch timeInterval { + case .minutes(let minute): + shouldSync = elapsedTimeInMinutes >= minute + } + } + else { + + shouldSync = true + } + + if shouldSync { + + lastSync = Date() + } + + return shouldSync + } + + func setTimeInterval(timeInterval: SyncInvalidatorTimeInterval) { + self.timeInterval = timeInterval + } +} diff --git a/godtools/App/Share/Common/SyncInvalidator/SyncInvalidatorManager.swift b/godtools/App/Share/Common/SyncInvalidator/SyncInvalidatorManager.swift new file mode 100644 index 0000000000..15123a102d --- /dev/null +++ b/godtools/App/Share/Common/SyncInvalidator/SyncInvalidatorManager.swift @@ -0,0 +1,46 @@ +// +// SyncInvalidatorManager.swift +// godtools +// +// Created by Levi Eggert on 5/8/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +class SyncInvalidatorManager { + + typealias ManagerId = String + typealias SyncInvalidatorId = String + + private static let defaultManagerId: ManagerId = "default_sync_invalidator_manager" + + private static var managers: [ManagerId: [SyncInvalidatorId: SyncInvalidator]] = Dictionary() + + static func getInvalidator(managerId: ManagerId = SyncInvalidatorManager.defaultManagerId, id: SyncInvalidatorId, timeInterval: SyncInvalidatorTimeInterval) -> SyncInvalidator { + + let syncInvalidator: SyncInvalidator + + if let existingSyncInvalidator = managers[managerId]?[id] { + syncInvalidator = existingSyncInvalidator + } + else { + syncInvalidator = SyncInvalidator(id: id, timeInterval: timeInterval) + } + + syncInvalidator.setTimeInterval(timeInterval: timeInterval) + + addSyncInvalidator(managerId: managerId, syncInvalidator: syncInvalidator) + + return syncInvalidator + } + + private static func addSyncInvalidator(managerId: ManagerId = SyncInvalidatorManager.defaultManagerId, syncInvalidator: SyncInvalidator) { + + var manager: [SyncInvalidatorId: SyncInvalidator] = managers[managerId] ?? Dictionary() + + manager[syncInvalidator.id] = syncInvalidator + + managers[managerId] = manager + } +} diff --git a/godtools/App/Share/Common/SyncInvalidator/SyncInvalidatorTimeInterval.swift b/godtools/App/Share/Common/SyncInvalidator/SyncInvalidatorTimeInterval.swift new file mode 100644 index 0000000000..011556ab29 --- /dev/null +++ b/godtools/App/Share/Common/SyncInvalidator/SyncInvalidatorTimeInterval.swift @@ -0,0 +1,14 @@ +// +// SyncInvalidatorTimeInterval.swift +// godtools +// +// Created by Levi Eggert on 5/7/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +enum SyncInvalidatorTimeInterval { + + case minutes(minute: TimeInterval) +} diff --git a/godtools/App/Share/Data-DomainInterface/GetInterfaceStringForLanguageRepository.swift b/godtools/App/Share/Data-DomainInterface/GetInterfaceStringForLanguageRepository.swift index c525312594..d52185c9bd 100644 --- a/godtools/App/Share/Data-DomainInterface/GetInterfaceStringForLanguageRepository.swift +++ b/godtools/App/Share/Data-DomainInterface/GetInterfaceStringForLanguageRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices @available(*, deprecated) class GetInterfaceStringForLanguageRepository: GetInterfaceStringForLanguageRepositoryInterface { diff --git a/godtools/App/Share/Data-DomainInterface/GetSearchBarInterfaceStringsRepository.swift b/godtools/App/Share/Data-DomainInterface/GetSearchBarInterfaceStringsRepository.swift index d171d558c4..9ca2d96054 100644 --- a/godtools/App/Share/Data-DomainInterface/GetSearchBarInterfaceStringsRepository.swift +++ b/godtools/App/Share/Data-DomainInterface/GetSearchBarInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetSearchBarInterfaceStringsRepository: GetSearchBarInterfaceStringsRepositoryInterface { diff --git a/godtools/App/Share/Data-DomainInterface/Supporting/GetToolListItemInterfaceStringsRepository/GetToolListItemInterfaceStringsRepository.swift b/godtools/App/Share/Data-DomainInterface/Supporting/GetToolListItemInterfaceStringsRepository/GetToolListItemInterfaceStringsRepository.swift index fb96401577..5cbbed9597 100644 --- a/godtools/App/Share/Data-DomainInterface/Supporting/GetToolListItemInterfaceStringsRepository/GetToolListItemInterfaceStringsRepository.swift +++ b/godtools/App/Share/Data-DomainInterface/Supporting/GetToolListItemInterfaceStringsRepository/GetToolListItemInterfaceStringsRepository.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices class GetToolListItemInterfaceStringsRepository { diff --git a/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/GetTranslatedLanguageName.swift b/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/GetTranslatedLanguageName.swift index 8415164790..1c4c6a1dc1 100644 --- a/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/GetTranslatedLanguageName.swift +++ b/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/GetTranslatedLanguageName.swift @@ -7,17 +7,18 @@ // import Foundation +import LocalizationServices class GetTranslatedLanguageName { - private let localizationServices: LocalizationServices - private let localeLanguageName: LocaleLanguageName - private let localeRegionName: LocaleLanguageRegionName - private let localeScriptName: LocaleLanguageScriptName + private let localizationLanguageNameRepository: LocalizationLanguageNameRepositoryInterface + private let localeLanguageName: LocaleLanguageNameInterface + private let localeRegionName: LocaleLanguageRegionNameInterface + private let localeScriptName: LocaleLanguageScriptNameInterface - init(localizationServices: LocalizationServices, localeLanguageName: LocaleLanguageName, localeRegionName: LocaleLanguageRegionName, localeScriptName: LocaleLanguageScriptName) { + init(localizationLanguageNameRepository: LocalizationLanguageNameRepositoryInterface, localeLanguageName: LocaleLanguageNameInterface, localeRegionName: LocaleLanguageRegionNameInterface, localeScriptName: LocaleLanguageScriptNameInterface) { - self.localizationServices = localizationServices + self.localizationLanguageNameRepository = localizationLanguageNameRepository self.localeLanguageName = localeLanguageName self.localeRegionName = localeRegionName self.localeScriptName = localeScriptName @@ -29,7 +30,7 @@ class GetTranslatedLanguageName { return language.fallbackName } - if let localizedName = getLanguageNameFromLocalization(language: language, translatedInLanguage: translatedInLanguage) { + if let localizedName = localizationLanguageNameRepository.getLanguageName(languageId: language.localeId, translatedInLanguage: translatedInLanguage), !localizedName.isEmpty { return localizedName } @@ -41,18 +42,6 @@ class GetTranslatedLanguageName { return language.fallbackName } - private func getLanguageNameFromLocalization(language: TranslatableLanguage, translatedInLanguage: BCP47LanguageIdentifier) -> String? { - - let localizedKey: String = "language_name_" + language.localeId - let localizedName: String = localizationServices.stringForLocaleElseEnglish(localeIdentifier: translatedInLanguage, key: localizedKey) - - guard !localizedName.isEmpty && localizedName != localizedKey else { - return nil - } - - return localizedName - } - private func getLanguageNameFromLocale(language: TranslatableLanguage, translatedInLanguage: BCP47LanguageIdentifier) -> String? { let languageName: String? @@ -63,7 +52,7 @@ class GetTranslatedLanguageName { languageName = localeLanguageName } else { - languageName = Locale(identifier: translatedInLanguage).localizedString(forIdentifier: language.localeId) + languageName = localeLanguageName.getLanguageName(forLocaleId: language.localeId, translatedInLanguageId: translatedInLanguage) } guard let languageName = languageName else { diff --git a/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedToolCategory/GetTranslatedToolCategory.swift b/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedToolCategory/GetTranslatedToolCategory.swift index cf062a185b..b0780217d0 100644 --- a/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedToolCategory/GetTranslatedToolCategory.swift +++ b/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedToolCategory/GetTranslatedToolCategory.swift @@ -7,6 +7,7 @@ // import Foundation +import LocalizationServices class GetTranslatedToolCategory { @@ -29,8 +30,19 @@ class GetTranslatedToolCategory { } func getTranslatedCategory(resource: ResourceModel, translateInLanguage: BCP47LanguageIdentifier) -> String { - - let category: String = localizationServices.stringForLocaleElseEnglish(localeIdentifier: translateInLanguage, key: "tool_category_\(resource.attrCategory)") + + let localeId: String + + if resource.supportsLanguage(languageId: translateInLanguage) { + localeId = translateInLanguage + } else { + localeId = resource.attrDefaultLocale + } + + let category: String = localizationServices.stringForLocaleElseEnglish( + localeIdentifier: localeId, + key: "tool_category_\(resource.attrCategory)" + ) return category } diff --git a/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedToolLanguageAvailability/GetTranslatedToolLanguageAvailability.swift b/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedToolLanguageAvailability/GetTranslatedToolLanguageAvailability.swift index 4dbf39331b..4afb50dcdc 100644 --- a/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedToolLanguageAvailability/GetTranslatedToolLanguageAvailability.swift +++ b/godtools/App/Share/Data-DomainInterface/Supporting/GetTranslatedToolLanguageAvailability/GetTranslatedToolLanguageAvailability.swift @@ -7,20 +7,21 @@ // import Foundation +import LocalizationServices class GetTranslatedToolLanguageAvailability { private let localizationServices: LocalizationServices private let resourcesRepository: ResourcesRepository private let languagesRepository: LanguagesRepository - private let translatedLanguageNameRepository: TranslatedLanguageNameRepository + private let getTranslatedLanguageName: GetTranslatedLanguageName - init(localizationServices: LocalizationServices, resourcesRepository: ResourcesRepository, languagesRepository: LanguagesRepository, translatedLanguageNameRepository: TranslatedLanguageNameRepository) { + init(localizationServices: LocalizationServices, resourcesRepository: ResourcesRepository, languagesRepository: LanguagesRepository, getTranslatedLanguageName: GetTranslatedLanguageName) { self.localizationServices = localizationServices self.resourcesRepository = resourcesRepository self.languagesRepository = languagesRepository - self.translatedLanguageNameRepository = translatedLanguageNameRepository + self.getTranslatedLanguageName = getTranslatedLanguageName } private var failedToDetermineLanguageAvailability: ToolLanguageAvailabilityDomainModel { @@ -56,7 +57,7 @@ class GetTranslatedToolLanguageAvailability { func getTranslatedLanguageAvailability(resource: ResourceModel, language: LanguageModel, translateInLanguage: LanguageModel) -> ToolLanguageAvailabilityDomainModel { - let translatedLanguageName: String = translatedLanguageNameRepository.getLanguageName(language: language, translatedInLanguage: translateInLanguage.code) + let translatedLanguageName: String = getTranslatedLanguageName.getLanguageName(language: language, translatedInLanguage: translateInLanguage.code) let resourceSupportsLanguage: Bool = resource.supportsLanguage(languageId: language.id) diff --git a/godtools/App/Share/Data/LanguageSettingsRepository/Cache/LanguageSettingsCache.swift b/godtools/App/Share/Data/LanguageSettingsRepository/Cache/LanguageSettingsCache.swift deleted file mode 100644 index 0e19973af8..0000000000 --- a/godtools/App/Share/Data/LanguageSettingsRepository/Cache/LanguageSettingsCache.swift +++ /dev/null @@ -1,92 +0,0 @@ -// -// LanguageSettingsCache.swift -// godtools -// -// Created by Levi Eggert on 7/29/22. -// Copyright © 2022 Cru. All rights reserved. -// - -import Foundation -import Combine - -class LanguageSettingsCache { - - private let userDefaults: UserDefaults = UserDefaults.standard - - init() { - - } - - func getPrimaryLanguageChanged() -> AnyPublisher { - return userDefaults.publisher(for: \.primaryLanguageId) - .eraseToAnyPublisher() - } - - func getParallelLanguageChanged() -> AnyPublisher { - return userDefaults.publisher(for: \.parallelLanguageId) - .eraseToAnyPublisher() - } - - func getPrimaryLanguageId() -> String? { - - guard let languageId = userDefaults.primaryLanguageId else { - return nil - } - - return languageId - } - - func getParallelLanguageId() -> String? { - - guard let languageId = userDefaults.parallelLanguageId else { - return nil - } - - return languageId - } - - func storePrimaryLanguage(id: String?) { - - userDefaults.primaryLanguageId = id - } - - func storeParallelLanguage(id: String?) { - - userDefaults.parallelLanguageId = id - } - - func deleteParallelLanguage() { - - storeParallelLanguage(id: nil) - } -} - -private extension UserDefaults { - - private static let primaryLanguageCacheKey: String = "kPrimaryLanguageId" - private static let parallelLanguageCacheKey: String = "kParallelLanguageId" - - @objc dynamic var primaryLanguageId: String? { - - get { - return string(forKey: UserDefaults.primaryLanguageCacheKey) - } - set { - - set(newValue, forKey: UserDefaults.primaryLanguageCacheKey) - synchronize() - } - } - - @objc dynamic var parallelLanguageId: String? { - - get { - return string(forKey: UserDefaults.parallelLanguageCacheKey) - } - set { - - set(newValue, forKey: UserDefaults.parallelLanguageCacheKey) - synchronize() - } - } -} diff --git a/godtools/App/Share/Data/LanguageSettingsRepository/LanguageSettingsRepository.swift b/godtools/App/Share/Data/LanguageSettingsRepository/LanguageSettingsRepository.swift deleted file mode 100644 index 9c16d60f50..0000000000 --- a/godtools/App/Share/Data/LanguageSettingsRepository/LanguageSettingsRepository.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// LanguageSettingsRepository.swift -// godtools -// -// Created by Levi Eggert on 7/29/22. -// Copyright © 2022 Cru. All rights reserved. -// - -import Foundation -import Combine - -@available(*, deprecated) // TODO: This will need to be removed once we finish refactor for tracking analytics property cru_contentlanguagesecondary in GT-2135. ~Levi -class LanguageSettingsRepository { - - private let cache: LanguageSettingsCache - - init(cache: LanguageSettingsCache) { - - self.cache = cache - } - - func getPrimaryLanguageChanged() -> AnyPublisher { - return cache.getPrimaryLanguageChanged() - .map { _ in - return () - } - .eraseToAnyPublisher() - } - - func getParallelLanguageChanged() -> AnyPublisher { - return cache.getParallelLanguageChanged() - } - - func getPrimaryLanguageId() -> String? { - - return cache.getPrimaryLanguageId() - } - - func getParallelLanguageId() -> String? { - - return cache.getParallelLanguageId() - } - - func storePrimaryLanguage(id: String) { - - cache.storePrimaryLanguage(id: id) - } - - func storeParallelLanguage(id: String) { - - cache.storeParallelLanguage(id: id) - } - - func deleteParallelLanguage() { - - cache.deleteParallelLanguage() - } -} diff --git a/godtools/App/Share/Data/LocalizationLanguageNameRepository/LocalizationLanguageNameRepository.swift b/godtools/App/Share/Data/LocalizationLanguageNameRepository/LocalizationLanguageNameRepository.swift new file mode 100644 index 0000000000..be2333b75e --- /dev/null +++ b/godtools/App/Share/Data/LocalizationLanguageNameRepository/LocalizationLanguageNameRepository.swift @@ -0,0 +1,39 @@ +// +// LocalizationLanguageNameRepository.swift +// godtools +// +// Created by Levi Eggert on 7/5/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +import LocalizationServices + +class LocalizationLanguageNameRepository: LocalizationLanguageNameRepositoryInterface { + + private static let supportedLanguageIds: [BCP47LanguageIdentifier] = ["fa", "fil-x-taglish", "sid"] // NOTE: If this list grows too large it could impact performance where UI lists of language names are displayed since it would require opening a bundle for every language in this list. ~Levi + + private let localizationServices: LocalizationServicesInterface + + init(localizationServices: LocalizationServicesInterface) { + + self.localizationServices = localizationServices + } + + func getLanguageName(languageId: BCP47LanguageIdentifier, translatedInLanguage: BCP47LanguageIdentifier) -> String? { + + guard LocalizationLanguageNameRepository.supportedLanguageIds.contains(languageId) else { + return nil + } + + let localizedKey: String = "language_name_" + languageId + + let localizedName: String = localizationServices.stringForLocaleElseEnglish(localeIdentifier: translatedInLanguage, key: localizedKey) + + if localizedName.isEmpty || localizedName == localizedKey { + return nil + } + + return localizedName + } +} diff --git a/godtools/App/Share/Data/LocalizationLanguageNameRepository/LocalizationLanguageNameRepositoryInterface.swift b/godtools/App/Share/Data/LocalizationLanguageNameRepository/LocalizationLanguageNameRepositoryInterface.swift new file mode 100644 index 0000000000..09f97d0c49 --- /dev/null +++ b/godtools/App/Share/Data/LocalizationLanguageNameRepository/LocalizationLanguageNameRepositoryInterface.swift @@ -0,0 +1,14 @@ +// +// LocalizationLanguageNameRepositoryInterface.swift +// godtools +// +// Created by Levi Eggert on 7/5/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation + +protocol LocalizationLanguageNameRepositoryInterface { + + func getLanguageName(languageId: BCP47LanguageIdentifier, translatedInLanguage: BCP47LanguageIdentifier) -> String? +} diff --git a/godtools/App/Share/Data/LocalizationServices/Bundle/LocaleLocalizableStringsBundle.swift b/godtools/App/Share/Data/LocalizationServices/Bundle/LocaleLocalizableStringsBundle.swift deleted file mode 100644 index 7b3aee999b..0000000000 --- a/godtools/App/Share/Data/LocalizationServices/Bundle/LocaleLocalizableStringsBundle.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// LocaleLocalizableStringsBundle.swift -// godtools -// -// Created by Levi Eggert on 8/1/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation - -class LocaleLocalizableStringsBundle: LocalizableStringsBundle { - - let localeBundleLoader: LocalizableStringsBundleLoader - let locale: Locale - let localeIdentifier: String - - init?(localeIdentifier: String, localeBundleLoader: LocalizableStringsBundleLoader, fileType: LocalizableStringsFileType) { - - self.localeBundleLoader = localeBundleLoader - self.locale = Locale(identifier: localeIdentifier) - self.localeIdentifier = localeIdentifier - - if let localeBundle = localeBundleLoader.bundleForResource(resourceName: localeIdentifier, fileType: fileType) { - super.init(bundle: localeBundle.bundle, fileType: fileType) - } - else { - return nil - } - } - - static func loadFromMainBundle(localeIdentifier: String, fileType: LocalizableStringsFileType) -> LocaleLocalizableStringsBundle? { - - let bundleLoader: LocalizableStringsBundleLoader = LocalizableStringsBundleLoader(localizableStringsFilesBundle: Bundle.main) - - return LocaleLocalizableStringsBundle( - localeIdentifier: localeIdentifier, - localeBundleLoader: bundleLoader, - fileType: fileType - ) - } - - override init(bundle: Bundle, fileType: LocalizableStringsFileType) { - fatalError("init(bundle:) has not been implemented") - } -} diff --git a/godtools/App/Share/Data/LocalizationServices/Bundle/LocalizableStringsBundle.swift b/godtools/App/Share/Data/LocalizationServices/Bundle/LocalizableStringsBundle.swift deleted file mode 100644 index 9a40a522b7..0000000000 --- a/godtools/App/Share/Data/LocalizationServices/Bundle/LocalizableStringsBundle.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// LocalizableStringsBundle.swift -// godtools -// -// Created by Levi Eggert on 8/1/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation - -class LocalizableStringsBundle { - - private static let uniqueValue: String = UUID().uuidString - - let bundle: Bundle - let fileType: LocalizableStringsFileType - - init(bundle: Bundle, fileType: LocalizableStringsFileType) { - - self.bundle = bundle - self.fileType = fileType - } - - func stringForKey(key: String) -> String? { - - let localizedString: String = bundle.localizedString(forKey: key, value: LocalizableStringsBundle.uniqueValue, table: nil) - - guard localizedString != LocalizableStringsBundle.uniqueValue else { - return nil - } - - guard !localizedString.isEmpty else { - return nil - } - - return localizedString - } -} diff --git a/godtools/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsBundleLoader.swift b/godtools/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsBundleLoader.swift deleted file mode 100644 index bc646bc5c8..0000000000 --- a/godtools/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsBundleLoader.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// LocalizableStringsBundleLoader.swift -// godtools -// -// Created by Levi Eggert on 8/1/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation - -class LocalizableStringsBundleLoader { - - // NOTE: Will be Base if Use Base Internationalization is checked in Project > Info > Localizations Section. - // English.lproj is deprecated for Localizable.strings. ~Levi - static let englishLocalizableStringsFile: String = "Base" - - // NOTE: Stringsdict still uses deprecated English.lproj. ~Levi - static let englishLocalizableStringsdictFile: String = "English" - - let localizableStringsFilesBundle: Bundle - - init(localizableStringsFilesBundle: Bundle?) { - - self.localizableStringsFilesBundle = localizableStringsFilesBundle ?? Bundle.main - } - - func getEnglishBundle(fileType: LocalizableStringsFileType) -> LocalizableStringsBundle? { - - guard let englishBundle = bundleForResource(resourceName: "en", fileType: fileType) else { - return nil - } - - return englishBundle - } - - func bundleForResource(resourceName: String, fileType: LocalizableStringsFileType) -> LocalizableStringsBundle? { - - guard !resourceName.isEmpty else { - return nil - } - - let possibleEnglishLocalizableStringsFiles: [String] = ["en", "english", "base"] - let isEnglishResource: Bool = possibleEnglishLocalizableStringsFiles.contains(resourceName.lowercased()) - - let localizationFilePath: String - - if isEnglishResource { - - switch fileType { - - case .strings: - localizationFilePath = LocalizableStringsBundleLoader.englishLocalizableStringsFile - - case .stringsdict: - localizationFilePath = LocalizableStringsBundleLoader.englishLocalizableStringsdictFile - } - } - else { - - localizationFilePath = resourceName - } - - guard let bundlePath = localizableStringsFilesBundle.path(forResource: localizationFilePath, ofType: "lproj") else { - return nil - } - - guard let bundle = Bundle(path: bundlePath) else { - return nil - } - - let localizableStringsBundle = LocalizableStringsBundle(bundle: bundle, fileType: fileType) - - return localizableStringsBundle - } -} diff --git a/godtools/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsFileType.swift b/godtools/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsFileType.swift deleted file mode 100644 index c3d1a430ee..0000000000 --- a/godtools/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsFileType.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// LocalizableStringsFileType.swift -// godtools -// -// Created by Levi Eggert on 8/1/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation - -enum LocalizableStringsFileType { - - case strings - case stringsdict -} diff --git a/godtools/App/Share/Data/LocalizationServices/LocalizableStringsRepository.swift b/godtools/App/Share/Data/LocalizationServices/LocalizableStringsRepository.swift deleted file mode 100644 index 9e63b9b388..0000000000 --- a/godtools/App/Share/Data/LocalizationServices/LocalizableStringsRepository.swift +++ /dev/null @@ -1,172 +0,0 @@ -// -// LocalizableStringsRepository.swift -// godtools -// -// Created by Levi Eggert on 8/9/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation - -class LocalizableStringsRepository { - - private var lastLoadedEnglishLocalizableStringsBundle: LocalizableStringsBundle? - private var lastLoadedSystemLocalizableStringsBundle: LocaleLocalizableStringsBundle? - private var lastLoadedLocaleLocalizableStringsBundle: LocaleLocalizableStringsBundle? - - let localizableStringsBundleLoader: LocalizableStringsBundleLoader - let fileType: LocalizableStringsFileType - - init(localizableStringsBundleLoader: LocalizableStringsBundleLoader, fileType: LocalizableStringsFileType) { - - self.localizableStringsBundleLoader = localizableStringsBundleLoader - self.fileType = fileType - } - - func stringForEnglish(key: String) -> String? { - - return getEnglishLocalizableStringsBundle()?.stringForKey(key: key) - } - - func stringForSystem(key: String) -> String? { - - return getSystemLocalizableStringsBundle()?.stringForKey(key: key) - } - - func stringForSystemElseEnglish(key: String) -> String? { - - if let systemString = stringForSystem(key: key) { - - return systemString - } - else if let englishString = stringForEnglish(key: key) { - - return englishString - } - - return nil - } - - func stringForLocale(localeIdentifier: String?, key: String) -> String? { - - guard let localeIdentifier = localeIdentifier, !localeIdentifier.isEmpty else { - return nil - } - - return getLocaleStringsBundle(localeIdentifier: localeIdentifier)?.stringForKey(key: key) - } - - func stringForLocaleElseEnglish(localeIdentifier: String?, key: String) -> String? { - - if let localeString = stringForLocale(localeIdentifier: localeIdentifier, key: key) { - - return localeString - } - else if let englishString = stringForEnglish(key: key) { - - return englishString - } - - return nil - } - - func stringForLocaleElseSystemElseEnglish(localeIdentifier: String?, key: String) -> String? { - - if let localeString = stringForLocale(localeIdentifier: localeIdentifier, key: key) { - - return localeString - } - else if let systemString = stringForSystem(key: key) { - - return systemString - } - else if let englishString = stringForEnglish(key: key) { - - return englishString - } - - return nil - } -} - -// MARK: - English Bundle - -extension LocalizableStringsRepository { - - func getEnglishLocalizableStringsBundle() -> LocalizableStringsBundle? { - - if let lastLoadedEnglishLocalizableStringsBundle = self.lastLoadedEnglishLocalizableStringsBundle { - - return lastLoadedEnglishLocalizableStringsBundle - } - else if let englishBundle = localizableStringsBundleLoader.getEnglishBundle(fileType: fileType) { - - lastLoadedEnglishLocalizableStringsBundle = englishBundle - - return englishBundle - } - - return nil - } -} - -// MARK: - System Bundle - -extension LocalizableStringsRepository { - - func getSystemLocalizableStringsBundle() -> LocalizableStringsBundle? { - - let systemLocaleIdentifier: String = getSystemLocaleIdentifier() - let systemLocaleChanged: Bool = systemLocaleIdentifier != lastLoadedSystemLocalizableStringsBundle?.localeIdentifier - - if lastLoadedSystemLocalizableStringsBundle == nil || systemLocaleChanged { - - if let newSystemLocalizableStrings = LocaleLocalizableStringsBundle(localeIdentifier: systemLocaleIdentifier, localeBundleLoader: localizableStringsBundleLoader, fileType: fileType) { - - lastLoadedSystemLocalizableStringsBundle = newSystemLocalizableStrings - } - else if let currentSystemLocalizableStrings = self.lastLoadedSystemLocalizableStringsBundle, currentSystemLocalizableStrings.localeIdentifier != "en" { - - lastLoadedSystemLocalizableStringsBundle = LocaleLocalizableStringsBundle(localeIdentifier: "en", localeBundleLoader: localizableStringsBundleLoader, fileType: fileType) - } - } - - return lastLoadedSystemLocalizableStringsBundle - } - - private func getSystemLocaleIdentifier() -> String { - - let localizationFilesBundle: Bundle = localizableStringsBundleLoader.localizableStringsFilesBundle - let preferredLocalizations: [String] = Bundle.preferredLocalizations(from: localizationFilesBundle.localizations, forPreferences: Locale.preferredLanguages) - - return preferredLocalizations.first ?? Locale.current.identifier - } -} - -// MARK: - Locale Bundle - -extension LocalizableStringsRepository { - - func getLocaleStringsBundle(localeIdentifier: String) -> LocalizableStringsBundle? { - - guard !localeIdentifier.isEmpty else { - return nil - } - - if let lastLoadedLocaleLocalizableStringsBundle = self.lastLoadedLocaleLocalizableStringsBundle, - lastLoadedLocaleLocalizableStringsBundle.localeIdentifier.lowercased() == localeIdentifier.lowercased() { - - return lastLoadedLocaleLocalizableStringsBundle - } - else { - - lastLoadedLocaleLocalizableStringsBundle = LocaleLocalizableStringsBundle( - localeIdentifier: localeIdentifier, - localeBundleLoader: localizableStringsBundleLoader, - fileType: fileType - ) - - return lastLoadedLocaleLocalizableStringsBundle - } - } -} diff --git a/godtools/App/Share/Data/LocalizationServices/LocalizationServices.swift b/godtools/App/Share/Data/LocalizationServices/LocalizationServices.swift deleted file mode 100644 index db42283285..0000000000 --- a/godtools/App/Share/Data/LocalizationServices/LocalizationServices.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// LocalizationServices.swift -// godtools -// -// Created by Levi Eggert on 6/18/20. -// Copyright © 2020 Cru. All rights reserved. -// - -import Foundation - -class LocalizationServices: LocalizationServicesInterface { - - private let stringsRepositories: [LocalizableStringsFileType: LocalizableStringsRepository] - - let bundleLoader: LocalizableStringsBundleLoader - - init(localizableStringsFilesBundle: Bundle?) { - - self.bundleLoader = LocalizableStringsBundleLoader(localizableStringsFilesBundle: localizableStringsFilesBundle) - - stringsRepositories = [ - .strings: LocalizableStringsRepository(localizableStringsBundleLoader: bundleLoader, fileType: .strings), - .stringsdict: LocalizableStringsRepository(localizableStringsBundleLoader: bundleLoader, fileType: .stringsdict) - ] - } - - func stringForEnglish(key: String, fileType: LocalizableStringsFileType = .strings) -> String { - - return stringsRepositories[fileType]?.stringForEnglish(key: key) ?? key - } - - func stringForSystemElseEnglish(key: String, fileType: LocalizableStringsFileType = .strings) -> String { - - return stringsRepositories[fileType]?.stringForSystemElseEnglish(key: key) ?? key - } - - func stringForLocaleElseEnglish(localeIdentifier: String?, key: String, fileType: LocalizableStringsFileType = .strings) -> String { - - return stringsRepositories[fileType]?.stringForLocaleElseEnglish(localeIdentifier: localeIdentifier, key: key) ?? key - } - - func stringForLocaleElseSystemElseEnglish(localeIdentifier: String?, key: String, fileType: LocalizableStringsFileType = .strings) -> String { - - return stringsRepositories[fileType]?.stringForLocaleElseSystemElseEnglish(localeIdentifier: localeIdentifier, key: key) ?? key - } -} diff --git a/godtools/App/Share/Data/LocalizationServices/LocalizationServicesInterface.swift b/godtools/App/Share/Data/LocalizationServices/LocalizationServicesInterface.swift deleted file mode 100644 index 71b299886a..0000000000 --- a/godtools/App/Share/Data/LocalizationServices/LocalizationServicesInterface.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// LocalizationServicesInterface.swift -// godtools -// -// Created by Levi Eggert on 3/14/24. -// Copyright © 2024 Cru. All rights reserved. -// - -import Foundation - -protocol LocalizationServicesInterface { - - func stringForEnglish(key: String, fileType: LocalizableStringsFileType) -> String - func stringForSystemElseEnglish(key: String, fileType: LocalizableStringsFileType) -> String - func stringForLocaleElseEnglish(localeIdentifier: String?, key: String, fileType: LocalizableStringsFileType) -> String - func stringForLocaleElseSystemElseEnglish(localeIdentifier: String?, key: String, fileType: LocalizableStringsFileType) -> String -} diff --git a/godtools/App/Share/Data/RealmDatabase/Configuration/RealmDatabaseProductionConfiguration.swift b/godtools/App/Share/Data/RealmDatabase/Configuration/RealmDatabaseProductionConfiguration.swift index c8cfeb94d3..7f12b2ad64 100644 --- a/godtools/App/Share/Data/RealmDatabase/Configuration/RealmDatabaseProductionConfiguration.swift +++ b/godtools/App/Share/Data/RealmDatabase/Configuration/RealmDatabaseProductionConfiguration.swift @@ -12,7 +12,7 @@ import RealmSwift class RealmDatabaseProductionConfiguration: RealmDatabaseConfiguration { private static let diskFileName: String = "godtools_realm" - private static let schemaVersion: UInt64 = 31 + private static let schemaVersion: UInt64 = 32 init() { diff --git a/godtools/App/Share/Data/RealmDatabase/RealmDatabase+Delete.swift b/godtools/App/Share/Data/RealmDatabase/RealmDatabase+Delete.swift index d1828a925b..f25c0a02e0 100644 --- a/godtools/App/Share/Data/RealmDatabase/RealmDatabase+Delete.swift +++ b/godtools/App/Share/Data/RealmDatabase/RealmDatabase+Delete.swift @@ -14,16 +14,26 @@ import Combine extension RealmDatabase { + func deleteObjects(realm: Realm, type: Element.Type, primaryKeyPath: String, primaryKeys: [String]) -> Error? { + + let results: Results = realm.objects(type) + .filter("\(primaryKeyPath) IN %@", primaryKeys) + + let objects: [ObjectBase] = Array(results) as? [ObjectBase] ?? Array() + + return deleteObjects(realm: realm, objects: objects) + } + func deleteObjectsInBackground(type: Element.Type, primaryKeyPath: String, primaryKeys: [String], completion: @escaping ((_ result: Result) -> Void)) { self.background { (realm: Realm) in - let results: Results = realm.objects(type) - .filter("\(primaryKeyPath) IN %@", primaryKeys) - - let objects: [ObjectBase] = Array(results) as? [ObjectBase] ?? Array() - - let deleteObjectsError: Error? = self.deleteObjects(realm: realm, objects: objects) + let deleteObjectsError: Error? = self.deleteObjects( + realm: realm, + type: type, + primaryKeyPath: primaryKeyPath, + primaryKeys: primaryKeys + ) if let deleteObjectsError = deleteObjectsError { diff --git a/godtools/App/Share/Data/ResourcesRepository/Cache/RealmResourcesCache.swift b/godtools/App/Share/Data/ResourcesRepository/Cache/RealmResourcesCache.swift index f39549cb65..1c242bc90a 100644 --- a/godtools/App/Share/Data/ResourcesRepository/Cache/RealmResourcesCache.swift +++ b/godtools/App/Share/Data/ResourcesRepository/Cache/RealmResourcesCache.swift @@ -157,7 +157,7 @@ extension RealmResourcesCache { extension RealmResourcesCache { - func getSpotlightTools() -> [ResourceModel] { + func getSpotlightTools(sortByDefaultOrder: Bool) -> [ResourceModel] { let realm: Realm = realmDatabase.openRealm() @@ -171,9 +171,20 @@ extension RealmResourcesCache { let filterPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: filterByAttributes) - let filteredResources = realm.objects(RealmResource.self).filter(filterPredicate) + let filteredResources: Results = realm.objects(RealmResource.self).filter(filterPredicate) + + let spotlightToolsResults: Results + + if sortByDefaultOrder { + + spotlightToolsResults = filteredResources.sorted(byKeyPath: #keyPath(RealmResource.attrDefaultOrder), ascending: true) + } + else { + + spotlightToolsResults = filteredResources + } - return filteredResources + return spotlightToolsResults .map { ResourceModel(model: $0) } diff --git a/godtools/App/Share/Data/ResourcesRepository/ResourcesRepository.swift b/godtools/App/Share/Data/ResourcesRepository/ResourcesRepository.swift index f9b35e92b6..33bb1a0fc7 100644 --- a/godtools/App/Share/Data/ResourcesRepository/ResourcesRepository.swift +++ b/godtools/App/Share/Data/ResourcesRepository/ResourcesRepository.swift @@ -157,8 +157,8 @@ class ResourcesRepository { extension ResourcesRepository { - func getSpotlightTools() -> [ResourceModel] { - return cache.getSpotlightTools() + func getSpotlightTools(sortByDefaultOrder: Bool = false) -> [ResourceModel] { + return cache.getSpotlightTools(sortByDefaultOrder: sortByDefaultOrder) } } diff --git a/godtools/App/Share/Data/TranslatedLanguageNameRepository/Cache/RealmTranslatedLanguageName.swift b/godtools/App/Share/Data/TranslatedLanguageNameRepository/Cache/RealmTranslatedLanguageName.swift deleted file mode 100644 index 6cb7f9cbb6..0000000000 --- a/godtools/App/Share/Data/TranslatedLanguageNameRepository/Cache/RealmTranslatedLanguageName.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// RealmTranslatedLanguageName.swift -// godtools -// -// Created by Levi Eggert on 1/16/24. -// Copyright © 2024 Cru. All rights reserved. -// - -import Foundation -import RealmSwift - -class RealmTranslatedLanguageName: Object { - - @objc dynamic var createdAt: Date = Date() - @objc dynamic var id: String = "" - @objc dynamic var language: BCP47LanguageIdentifier = "" - @objc dynamic var languageTranslation: BCP47LanguageIdentifier = "" - @objc dynamic var translatedName: String = "" - @objc dynamic var updatedAt: Date = Date() - - override static func primaryKey() -> String? { - return "id" - } -} diff --git a/godtools/App/Share/Data/TranslatedLanguageNameRepository/Cache/RealmTranslatedLanguageNameCache.swift b/godtools/App/Share/Data/TranslatedLanguageNameRepository/Cache/RealmTranslatedLanguageNameCache.swift deleted file mode 100644 index ac5944a625..0000000000 --- a/godtools/App/Share/Data/TranslatedLanguageNameRepository/Cache/RealmTranslatedLanguageNameCache.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// RealmTranslatedLanguageNameCache.swift -// godtools -// -// Created by Levi Eggert on 1/16/24. -// Copyright © 2024 Cru. All rights reserved. -// - -import Foundation -import RealmSwift - -class RealmTranslatedLanguageNameCache { - - private let realmDatabase: RealmDatabase - - init(realmDatabase: RealmDatabase) { - - self.realmDatabase = realmDatabase - } - - private func getPrimaryKey(language: TranslatableLanguage, languageTranslation: BCP47LanguageIdentifier) -> String? { - - let localeId: String = language.localeId - - guard !localeId.isEmpty && !languageTranslation.isEmpty else { - return nil - } - - return localeId.lowercased() + "-" + languageTranslation.lowercased() - } - - private func getTranslatedLanguageName(realm: Realm, language: TranslatableLanguage, languageTranslation: BCP47LanguageIdentifier) -> RealmTranslatedLanguageName? { - - guard let primaryKey = getPrimaryKey(language: language, languageTranslation: languageTranslation) else { - return nil - } - - return realm.object(ofType: RealmTranslatedLanguageName.self, forPrimaryKey: primaryKey) - } - - func getExistingTranslatedLanguageNameElseNew(realm: Realm, language: TranslatableLanguage, languageTranslation: BCP47LanguageIdentifier) -> RealmTranslatedLanguageName? { - - if let existingObject = getTranslatedLanguageName(realm: realm, language: language, languageTranslation: languageTranslation) { - - return existingObject - } - else if let primarykey = getPrimaryKey(language: language, languageTranslation: languageTranslation) { - - let newObject = RealmTranslatedLanguageName() - newObject.id = primarykey - - return newObject - } - - return nil - } - - func getTranslatedLanguageName(language: TranslatableLanguage, languageTranslation: BCP47LanguageIdentifier) -> TranslatedLanguageNameDataModel? { - - guard let realmObject = getTranslatedLanguageName(realm: realmDatabase.openRealm(), language: language.localeId, languageTranslation: languageTranslation) else { - return nil - } - - return TranslatedLanguageNameDataModel(realmObject: realmObject) - } - - func storeTranslatedLanguage(language: TranslatableLanguage, languageTranslation: BCP47LanguageIdentifier, translatedName: String) { - - guard let primaryKey = self.getPrimaryKey(language: language, languageTranslation: languageTranslation) else { - return - } - - realmDatabase.writeObjectsInBackground { (realm: Realm) -> [RealmTranslatedLanguageName] in - - let realmObject: RealmTranslatedLanguageName - - if let existingObject = self.getTranslatedLanguageName(realm: realm, language: language, languageTranslation: languageTranslation) { - - existingObject.translatedName = translatedName - existingObject.updatedAt = Date() - - realmObject = existingObject - } - else { - - let newObject = RealmTranslatedLanguageName() - - newObject.createdAt = Date() - newObject.id = primaryKey - newObject.language = language.localeId - newObject.languageTranslation = languageTranslation - newObject.translatedName = translatedName - newObject.updatedAt = Date() - - realmObject = newObject - } - - return [realmObject] - - } mapInBackgroundClosure: { (objects: [RealmTranslatedLanguageName]) -> [RealmTranslatedLanguageName] in - - return objects - - } completion: { (result: Result<[RealmTranslatedLanguageName], Error>) in - - } - } -} diff --git a/godtools/App/Share/Data/TranslatedLanguageNameRepository/Sync/TranslatedLanguageNameRepositorySync.swift b/godtools/App/Share/Data/TranslatedLanguageNameRepository/Sync/TranslatedLanguageNameRepositorySync.swift deleted file mode 100644 index 2d749e550c..0000000000 --- a/godtools/App/Share/Data/TranslatedLanguageNameRepository/Sync/TranslatedLanguageNameRepositorySync.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// TranslatedLanguageNameRepositorySync.swift -// godtools -// -// Created by Levi Eggert on 1/16/24. -// Copyright © 2024 Cru. All rights reserved. -// - -import Foundation -import Combine -import RealmSwift - -class TranslatedLanguageNameRepositorySync { - - private let realmDatabase: RealmDatabase - private let getTranslatedLanguageName: GetTranslatedLanguageName - private let cache: RealmTranslatedLanguageNameCache - - init(realmDatabase: RealmDatabase, getTranslatedLanguageName: GetTranslatedLanguageName, cache: RealmTranslatedLanguageNameCache) { - - self.realmDatabase = realmDatabase - self.getTranslatedLanguageName = getTranslatedLanguageName - self.cache = cache - } - - func syncTranslatedLanguageNamesPublisher(translateInLanguage: BCP47LanguageIdentifier, languages: [LanguageModel]) -> AnyPublisher { - - return realmDatabase.writeObjectsPublisher { (realm: Realm) -> [RealmTranslatedLanguageName] in - - var translatedLanguageNames: [RealmTranslatedLanguageName] = Array() - - for languageModel in languages { - - if let realmObject = self.getRealmTranslatedLanguage(realm: realm, language: languageModel, translateInLanguage: translateInLanguage) { - translatedLanguageNames.append(realmObject) - } - - if let realmObject = self.getRealmTranslatedLanguage(realm: realm, language: languageModel, translateInLanguage: languageModel.code) { - translatedLanguageNames.append(realmObject) - } - } - - return translatedLanguageNames - - } mapInBackgroundClosure: { (objects: [RealmTranslatedLanguageName]) -> [Void] in - return [Void()] - } - .map { _ in - return () - } - .catch ({ (error: Error) in - return Just(()) - .eraseToAnyPublisher() - }) - .eraseToAnyPublisher() - } - - private func getRealmTranslatedLanguage(realm: Realm, language: TranslatableLanguage, translateInLanguage: BCP47LanguageIdentifier) -> RealmTranslatedLanguageName? { - - guard let realmTranslatedLanguage = cache.getExistingTranslatedLanguageNameElseNew(realm: realm, language: language, languageTranslation: translateInLanguage) else { - return nil - } - - let translatedName: String = getTranslatedLanguageName.getLanguageName( - language: language, - translatedInLanguage: translateInLanguage - ) - - guard !translatedName.isEmpty else { - return nil - } - - realmTranslatedLanguage.language = language.localeId - realmTranslatedLanguage.languageTranslation = translateInLanguage - realmTranslatedLanguage.translatedName = translatedName - - return realmTranslatedLanguage - } -} diff --git a/godtools/App/Share/Data/TranslatedLanguageNameRepository/TranslatedLanguageNameDataModel.swift b/godtools/App/Share/Data/TranslatedLanguageNameRepository/TranslatedLanguageNameDataModel.swift deleted file mode 100644 index 85d82c8a10..0000000000 --- a/godtools/App/Share/Data/TranslatedLanguageNameRepository/TranslatedLanguageNameDataModel.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// TranslatedLanguageNameDataModel.swift -// godtools -// -// Created by Levi Eggert on 1/16/24. -// Copyright © 2024 Cru. All rights reserved. -// - -import Foundation - -struct TranslatedLanguageNameDataModel { - - let createdAt: Date - let id: String - let language: BCP47LanguageIdentifier - let languageTranslation: BCP47LanguageIdentifier - let translatedName: String - let updatedAt: Date - - init(realmObject: RealmTranslatedLanguageName) { - - createdAt = realmObject.createdAt - id = realmObject.id - language = realmObject.language - languageTranslation = realmObject.languageTranslation - translatedName = realmObject.translatedName - updatedAt = realmObject.updatedAt - } -} diff --git a/godtools/App/Share/Data/TranslatedLanguageNameRepository/TranslatedLanguageNameRepository.swift b/godtools/App/Share/Data/TranslatedLanguageNameRepository/TranslatedLanguageNameRepository.swift deleted file mode 100644 index 3ef7623ee5..0000000000 --- a/godtools/App/Share/Data/TranslatedLanguageNameRepository/TranslatedLanguageNameRepository.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// TranslatedLanguageNameRepository.swift -// godtools -// -// Created by Levi Eggert on 1/16/24. -// Copyright © 2024 Cru. All rights reserved. -// - -import Foundation -import Combine - -class TranslatedLanguageNameRepository { - - private let getTranslatedLanguageName: GetTranslatedLanguageName - private let cache: RealmTranslatedLanguageNameCache - - init(getTranslatedLanguageName: GetTranslatedLanguageName, cache: RealmTranslatedLanguageNameCache) { - - self.getTranslatedLanguageName = getTranslatedLanguageName - self.cache = cache - } - - func getLanguageName(language: TranslatableLanguage, translatedInLanguage: BCP47LanguageIdentifier) -> String { - - guard !translatedInLanguage.isEmpty else { - return language.fallbackName - } - - if let cachedObject = cache.getTranslatedLanguageName(language: language, languageTranslation: translatedInLanguage), !cachedObject.translatedName.isEmpty { - - return cachedObject.translatedName - } - - let translatedName: String = getTranslatedLanguageName.getLanguageName(language: language, translatedInLanguage: translatedInLanguage) - - cache.storeTranslatedLanguage( - language: language, - languageTranslation: translatedInLanguage, - translatedName: translatedName - ) - - return translatedName - } -} diff --git a/godtools/App/Share/Domain/UseCases/GetLanguageUseCase/GetLanguageUseCase.swift b/godtools/App/Share/Domain/UseCases/GetLanguageUseCase/GetLanguageUseCase.swift index 2083f70f91..1a35638a0c 100644 --- a/godtools/App/Share/Domain/UseCases/GetLanguageUseCase/GetLanguageUseCase.swift +++ b/godtools/App/Share/Domain/UseCases/GetLanguageUseCase/GetLanguageUseCase.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import LocalizationServices @available(*, deprecated) class GetLanguageUseCase { diff --git a/godtools/App/Share/Domain/UseCases/GetToolTranslationsFilesUseCase/DetermineToolTranslationsToDownload/DetermineToolTranslationsToDownload.swift b/godtools/App/Share/Domain/UseCases/GetToolTranslationsFilesUseCase/DetermineToolTranslationsToDownload/DetermineToolTranslationsToDownload.swift index 3a9d4dbd0a..2a6455434f 100644 --- a/godtools/App/Share/Domain/UseCases/GetToolTranslationsFilesUseCase/DetermineToolTranslationsToDownload/DetermineToolTranslationsToDownload.swift +++ b/godtools/App/Share/Domain/UseCases/GetToolTranslationsFilesUseCase/DetermineToolTranslationsToDownload/DetermineToolTranslationsToDownload.swift @@ -46,8 +46,9 @@ class DetermineToolTranslationsToDownload: DetermineToolTranslationsToDownloadTy translations.append(translation) } - if translations.isEmpty, let englishTranslation = translationsRepository.getLatestTranslation(resourceId: resourceId, languageCode: LanguageCodeDomainModel.english.value) { - translations = [englishTranslation] + if translations.isEmpty, let defaultTranslation = translationsRepository.getLatestTranslation(resourceId: resourceId, languageCode: resource.attrDefaultLocale) { + + translations = [defaultTranslation] } let result = DetermineToolTranslationsToDownloadResult(translations: translations) diff --git a/godtools/English.lproj/Localizable.stringsdict b/godtools/Base.lproj/Localizable.stringsdict similarity index 100% rename from godtools/English.lproj/Localizable.stringsdict rename to godtools/Base.lproj/Localizable.stringsdict diff --git a/godtools/am.lproj/Localizable.strings b/godtools/am.lproj/Localizable.strings index 1b6fe347b9..a92cf81c1e 100755 --- a/godtools/am.lproj/Localizable.strings +++ b/godtools/am.lproj/Localizable.strings @@ -1,32 +1,300 @@ +"language_name_fil-x-taglish" = "ታግሊሽ"; +"language_name_sid" = "ሲዳማ"; +"onboardingTutorial.chooseLanguageButton.title" = "ቋንቋ ምረጥ"; +"languageSettings.confirmAppLanguage.message" = "%@ የመተግበሪያ ዋና ቋንቋ አድርገህ መርጠሀል። ይህ የመላው GodTools መተግበሪያን ቋንቋ ይለውጣል። እርግጠኛ ነህ?"; +"languageSettings.confirmAppLanguage.changeLanguageButton.title" = "ቋንቋ ቀይር"; +"languageSettings.confirmAppLanguage.nevermindButton.title" = "ግድ የለም"; +"languageSettings.downloadableLanguages.title" = "ሊወርዱ የሚችሉ ቋንቋዎች"; +"languageSettings.appInterface.title" = "የመተግበሪያ በይነገጽ ቋንቋ"; +"languageSettings.appInterface.message" = "በመላው መተግበሪያ እንዲታይ የሚፈልጉትን ቋንቋ ያዘጋጁ።"; +"languageSettings.appInterface.languagesAvailable" = "የቀረቡ ቋንቋዎች"; +"languageSettings.toolLanguagesAvailableOffline.title" = "የመተግበሪያ ቋንቋዎች ያለኢንተርኔት የሚሰሩ"; +"languageSettings.toolLanguagesAvailableOffline.message" = "ከዋይፋይ ወይም ከአገልግሎት መስጫ ክልል ውጪ ቢሆኑም እንኳ መጠቀም እንዲችሉ በመረጡት ቋንቋ ቡክሌቶቹን ያውርዱ። የመተግበሪያ ቋንቋ የአማራጮች ቁልፍን በመጫን ይምረጡ።"; +"languageSettings.toolLanguagesAvailableOffline.editDownloadedLanguagesButton.title" = "የወረዱ ቋንቋዎችን ያስተካክሉ"; +"allTools.filter.title" = "አጣራ"; +"allTools.filter.anyCategory" = "ማንኛውም ምድብ"; +"allTools.filter.anyLanguage" = "ማንኛውም ቋንቋ"; +"allTools.filter.navBar.language" = "በቋንቋ አጣራ"; +"allTools.filter.navBar.category" = "በምድብ አጣራ"; +"languageSettings.appLanguage.title" = "የመተግበሪያ ቋንቋ"; +"authError.userAccountAlreadyExists.message" = "የተጠቃሚ መለያ አስቀድሞ አለ።"; +"authError.userAccountNotFound.message" = "የተጠቃሚ መለያ አልተገኘም።"; +"createAccount.title" = "መለያ ፍጠር"; +"createAccount.subtitle" = "እውነተኛ ታሪኮችን፣ ማበረታቻዎችን እና ተግባራዊ ምክሮችን በእጅዎ ለማግኘት መለያ ይፍጠሩ።"; +"deleteAccountProgress.title" = "መለያን በመሰረዝ ላይ..."; +"confirmDeleteAccount.title" = "እርግጠኛ ነህ?"; +"confirmDeleteAccount.confirmButton.title" = "መለያ አጥፋ"; +"accountDeletedAlert.title" = "መለያ ተሰርዟል"; +"accountDeletedAlert.message" = "መለያህ ተሰርዟል።"; +"deleteAccount.title" = "ቶሎ እንዳትሄድ"; +"deleteAccount.subtitle" = "መለያዎን ካጠትፉ የሚወዷቸውን መሳሪያዎች ወይም የመለያ እንቅስቃሴ ማግኘት አይችሉም።"; +"deleteAccount.confirmButton.title" = "መለያዬን አጥፋ"; +"deleteAccount.cancelButton.title" = "መለያዬን አታጥፋ"; +"signIn.title" = "ግባ"; +"signIn.subtitle" = "እውነተኛ ታሪኮችን፣ ማበረታቻዎችን እና ተግባራዊ ምክሮችን በእጅዎ ለማግኘት ወደ መለያዎ ይግቡ።"; +"signIn.google" = "በGoogle ይግቡ"; +"signIn.facebook" = "በFacebook ይግቡ"; +"signIn.apple" = "በApple ይቀጥሉ"; +"menu_getStarted" = "እንጀምር"; +"menu_support" = "ድጋፍ"; +"menu_about" = "ስለዚህ አፕሊኬሽን"; +"menu.sendFeedback" = "አስተያይት ላክ"; +"menu.reportABug" = "ስህተት ሪፖርት ያድርጉ"; +"menu.askAQuestion" = "ጥያቄ ጠይቅ"; +"menu.leaveAReview" = "አስተይያት ይፃፉ"; +"account.badges.sectionTitle" = "ባጆችህ"; +"account.activity.sectionTitle" = "የአንተ እንቅስቃሴ"; + +/* This is a title on a dashboard that will show the Number of Times a person has opened a tool */ +"account.activity.toolOpens" = "የተከፈቱ ቡክሌቶች"; + +/* This is a title on a dashboard that will show the Number of Times a person has completed a lesson */ +"account.activity.lessonCompletion" = "የትምህርት ማጠናቀቂያዎች"; + +/* This is a title on a dashboard that will show the Number of Times a person has used the screen share function */ +"account.activity.screenShares" = "ስክሪን ማጋራቶች"; + +/* This is a title on a dashboard that will show the Number of Times a person has shared a link */ +"account.activity.linksShared" = "ማስፈንጠሪያ ማጋራቶች"; + +/* This is a title on a dashboard that will show the Number of different languages they have used */ +"account.activity.languagesUsed" = "የተጠቀሙዋቸው ቋንቋዎች"; + +"account.activity.sessions" = "ክፍለ ጊዜዎች"; +"badges.tipsCompleted.singular" = "የተጠናቀቁ 1 የሥልጠና ምክሮች"; + +/* leave %d in phrase - this is the number of articles that the user has read */ +"badges.articlesOpened.plural" = "%d ጽሑፎችን ያንብቡ"; + +/* when they have read only 1 articles */ +"badges.articlesOpened.singular" = "1 ጽሑፍ ያንብቡ"; + +/* leave %d in phrase - this is the number of images that the user has shared */ +"badges.imagesShared.plural" = "የተጋሩ %d ምስሎች"; + +"badges.imagesShared.singular" = "የተጋሩ 1 ምስሎች"; + +/* leave %d in phrase - this is the number of lessons that the user has completed */ +"badges.lessonsCompleted.plural" = "የተጠናቀቁ %d ትምህርቶች"; + +"badges.lessonsCompleted.singular" = "የተጠናቀቁ 1 ትምህርቶች"; + +/* leave %d in phrase - this is the number of tools that the user has opened */ +"badges.toolsOpened.plural" = "%d መሳሪያዎች ተከፍተዋል"; + +"badges.toolsOpened.singular" = "1 መሳሪያዎች ተከፍተዋል"; + +/* leave %d in phrase - this is the number of tips that the user has completed */ +"badges.tipsCompleted.plural" = "የተጠናቀቁ %d የሥልጠና ምክሮች"; + +/* Just translate "Joined" - leave the %@ - this is the date they started using GodTools */ +"account.joinedOn" = "%@ ተቀላቅሏል"; + +"account.globalActivity.title" = "ዓለም አቀፋዊ እንቅስቃሴ"; +"toolDetails.conversationStarters.title" = "የውይይት መጀመሪያዎች"; + +/* A list of the main points of the tool will live behind a section called "Outline" */ +"toolDetails.outline.title" = "ማውጫ"; + +"toolDetails.bibleReferences.title" = "የመጽሐፍ ቅዱስ ማጣቀሻዎች"; + /* This About refers to the whole app. */ "aboutApp.navTitle" = "ስለዚህ አፕሊኬሽን"; /* This About refers to a specific tool in the app. */ "toolDetails.about.title" = "ስለ"; +/* This means that the tool/resource is not available in the currently selected language. */ +"lessonCard.languageNotAvailable" = "በ %@ ውስጥ አይገኝም"; + +"menu.deleteAccount" = "መለያ አጥፋ"; +"alert.mailAppUnavailable.title" = "የMail መተግበሪያ የለም"; +"alert.mailAppUnavailable.message" = "የMail መተግበሪያ በትክክል አልተዋቀረም ወይም በዚህ ስልክ ላይ አይገኝም። "; +"tool_menu_item.tools" = "ቡክሌቶች"; +"lessons.pageTitle" = "ለእርስዎ የተዘጋጁ ትምህርቶች"; +"lessons.pageSubtitle" = "ግንኙነቶችን ለመፍጠር እና ትርጉም ያለው ውይይት ለመጀመር ተግባራዊ ሀሳቦች።"; +"toolSettings.languagesAvailable.title" = "የቀረቡ ቋንቋዎች"; +"toolDetails.versions.message" = "ከዚህ በታች የዚህ መሣሪያ የተለየ ስሪት ይምረጡ። ለእያንዳንዱ እትም ልዩ ዘይቤ እና ይዘት ስላለው ለውይይትዎ የተሻለውን ማግኘት ይችላሉ።የአንድን ቡክሌት የተለያዩ ስሪቶችን ወደ ምርጫዎ ማስገባት ይችላሉ።"; +"favorites.noTools.title" = "የተመረጡ ቡክሌቶች የሉም"; +"favorites.noTools.description" = "ቡክሌቱን ወደ ምርጫዎ ለማስገባት እና ከኢንተርኔት ውጭ የሚገኝ ለማድረግ የልብ ምልክቱን ይንኩ።"; +"favorites.noTools.button" = "ወደ ቡክሌቶች ሂድ"; +"toolSettings.shareImagePreview.shareImageButton.title" = "ምስል አጋራ"; +"favorites.pageTitle" = "ወደ GodTools እንኳን በደህና መጡ"; +"favorites.favoriteLessons.title" = "ተለይቶ የቀረበ ትምህርት"; +"favorites.favoriteTools.title" = "የእርስዎ ተመራጭ ቡክሌቶች"; +"favorites.favoriteTools.viewAll" = "ሁሉንም ይመልከቱ "; +"favorites.favoriteLessons.details" = "ዝርዝሮች"; + +/* The meaning is "I don't want to set a second language". Language is no language or None */ +"toolSettings.languagesList.deleteLanguage.title" = "ምንም"; + +"toolSettings.chooseLanguage.toggleMessage" = "በዚህ መተግበሪያ ውስጥ በሁለት የተለያዩ ቋንቋዎች መካከል ይቀያይሩ።"; +"toolDetails.versions.title" = "ስሪቶች"; +"toolDetails.learnToShareToolButton.title" = "ይህን ቡክሌት ማጋራት ይማሩ"; +"toolSettings.title" = "የመተግበሪያ አማራጮች"; +"toolSettings.option.shareLink.title" = "መስፈንጠሪያ አጋራ"; +"toolSettings.option.screenShare.title" = "ስክሪን አጋራ"; +"toolSettings.option.trainingTips.show.title" = "የስልጠና ምክሮች"; +"toolSettings.option.trainingTips.hide.title" = "ጠቃሚ ምክሮችን ደብቅ"; +"toolSettings.chooseLanguage.title" = "ሁለት ቋንቋ"; +"toolSettings.chooseLanguage.noParallelLanguageTitle" = "ትይዪ"; +"toolSettings.shareables.title" = "ተዛማጅ ግራፊክስ"; +"allTools.spotlight.title" = "የተመረጡ ቡክሌቶች"; +"allTools.spotlight.description" = "ሊወዷቸው ይችልሉ ብለን ያሰብናቸውን ቡክሌቶች ከታች ያገኛሉ።"; +"training_tip_ask" = "ጠይቅ"; +"training_tip_consider" = "አስቡበት"; +"training_tip_prepare" = "አዘጋጅ"; +"training_tip_quote" = "ጥቅስ"; +"training_tip_tip" = "ጠቃሚ ምክር"; +"start_training" = "ስልጠናውን ጀምር"; +"close" = "ዝጋ"; +"tool_menu_item.lessons" = "ትምህርቶች"; "parallelLanguage.yesButton.title" = "አዎ"; "parallelLanguage.noButton.title" = "አይ አልፈልግም"; +"parallelLanguage.selectButton.title" = "ምረጥ"; +"parallelLanguage.getStartedButton.title" = "እንጀምር"; +"parallelLanguage.selectLanguageButton.title" = "ቋንቋ ምረጥ"; +"parellelLanguage.prompt" = "ሁለተኛ ቋንቋ መምረጥ ይፈልጋሉ?"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.continueButton.title.closeTutorial" = "አጋዥ ስልጠናን ዝጋ"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.tool.title" = "ቡክሌቶች"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.tool.message" = "እያንዳንዱ ቡክሌት ለተለየ የውይይት ጊዜያት የተሠራ ነው- ከውይይት በፊት ፣ ከውይይት ጊዜ ወይም ከውይይት በኋላ።"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.toolTip.title" = "የቡክሌት ምክሮች"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.toolTip.message" = "አንድ ሰው ወንጌልን እንዲረዳ እና ኢየሱስን የበለጠ እንዲያውቅ የGodTools ምክሮች ይረዳዎታል።"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.screenShare.title" = "ስክሪን ማጋራት"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.screenShare.message" = "አሁን ስክሪንህን ማጋራት ትችላለህ ስለዚህ ስለ ወንጌል ለመወያየት ቡክሌቱን ስትጠቀም ሌላው ሰው መከተል ይችላል።"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.lesson.title" = "ትምህርቶች"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.lesson.message" = "በእያንዳንዱ የGodTools ትምህርት ከእግዚአብሔር እውነተኛ ግንኙነቶች እንዲኖሯችሁ የሚረዳ አንድ ነገር ትማራላችሁ።"; + +/* This is a tutorial phrase and is optional to translate. The tutorial is not shown in every language. Usually just with those that have lessons and tips translated. */ +"tutorial.findTutorial.title" = "ይህንን አጋዥ ስልጠና በሚፈልጉበት ጊዜ በማውጫ ውስጥ ያግኙት።"; + +"onboardingTutorial.beginButton.title" = "ጀምር"; +"onboardingQuickStart.2.title" = "ለአንድ ሰው ወንጌልን ማካፈል እፈልጋለሁ"; +"onboardingQuickStart.2.button.title" = "ከቡክሌቶች ውስጥ አንዱን ይምረጡ"; +"onboardingTutorial.nextButton.title" = "ቀጣይ"; +"onboardingQuickStart.title" = "በፍጥነት ለመጀመር የሚረዱ ማስፈንጠሪያዎች"; +"onboardingQuickStart.0.title" = "ስለ ኢየሱስ የበለጠ ማወቅ እፈልጋለሁ።"; +"onboardingQuickStart.0.button.title" = "ጽሑፎቻችንን ያንብቡ"; +"onboardingQuickStart.1.title" = "ስለ ወንጌል ማውራትን መማር እፈልጋለሁ"; +"onboardingQuickStart.1.button.title" = "ትምህርቶቻችንን ይሞክሩ"; +"tool_offline_favorite_message" = "ወደ ምርጫዎ የሚያክለውን የልብ ምልክት በመጫን ቡክሌቱን ከመስመር ውጭ የሚገኝ ያድርጉት።"; + +/* This is a category description of a tool that helps you start a conversation */ +"tool_category_conversation_starter" = "የውይይት ጀማሪ"; + +/* This phrase describes a category of tool that helps someone grow in their Christian faith */ +"tool_category_growth" = "የክርስቲያን እድገት"; + +"share_link" = "መስፈንጠሪያ አጋራ"; +"share_tool_menu.remote_share_tool" = "ስክሪን ያጋሩ"; +"share_tool_menu.send_tool" = "ይህን ቡክሌት ላክ"; +"share_tool_screen_tutorial.share_your_screen.title" = "ስክሪን ያጋሩ"; +"share_tool_screen_tutorial.share_your_screen.message" = "ይህ አማራጭ ይህን ቡክሌት የሚከፍት ማስፈንጠሪያ ለጓደኛ ይልካል። ማስፈንጠሪያው ሲከፈት የስክሪን ማጋሪያ ክፍለ ጊዜ ይጀምራል።"; +"share_tool_screen_tutorial.mirrored_experience.title" = "የተጋራ ይዘት"; +"share_tool_screen_tutorial.mirrored_experience.message" = "ቡክሌቱን በስልክዎ ላይ ሲጠቀሙ፣ የጓደኛዎ ማያ ገጽ በተመሳሳይ መንገድ ምላሽ ይሰጣል። ይህ በአንድ ጊዜ በ 2 ስልኮች ላይ በአንድ መተግበሪያ ውስጥ እንዲጠቀሙ ያስችልዎታል።"; +"share_tool_screen_tutorial.get_started.title" = "እንጀምር"; +"share_tool_screen_tutorial.get_started.message" = "የስክሪን ማጋሪያ ክፍለ ጊዜ ለመጀመር ማስፈንጠሪያዉን ለአንድ ወይም ከዚያ በላይ ሰዎች ይላኩ።"; +"share_tool_remote_link_message" = "ሰላም፣ በ GodTools ውስጥ በስክሪን ማጋራት አብረውኝ ይከታተሉ። ይህን ማስፈንጠሪያ ይጠቀሙ፡ %@ + + ይህ መልእክት እስከ በኋላ ላይ ካልደረስክ፣ ማስፈንጠሪውያ አሁንም ላይሰራ ይችላል። በሌላ ጊዜ እንደገና መሞከር እንችላለን።"; +"exit_tract_remote_share_session.message" = "ስክሪን ከማጋራት አሁን መውጣት ይፈልጋሉ?"; +"load_tool_remote_session.message" = "ስክሪን ማጋራትን በመጀመር ላይ…"; +"learn_to_share_tool.learn_to_share.title" = "ይህን ቡክሌት ከሁሉም ጋር ማጋራት ይማሩ"; +"learn_to_share_tool.learn_to_share.message" = "ይህንን መተግበሪያ ለሌሎች በማጋራት በራስ የመተማመን ስሜት እንዲሰማዎት የሚፈልጉትን ያህል ጠቃሚ ምክሮችን በመጠቀም ይለማመዱ።"; +"learn_to_share_tool.tips_light_the_way.title" = "የበለጠ ሚያስተምሩ ምክሮች"; +"learn_to_share_tool.tips_light_the_way.message" = "በተመረጡ ስክሪኖች ላይ የተለያዩ ምልክቶችን ታያለህ። ስለ ስክሪኑ ይዘት ለመናገር የሚረዳዎትን ጠቃሚ ምክር ለማየት ምልክቱን ይጫኑ።"; +"learn_to_share_tool.light_up_the_tips.title" = "ምክሮቹን ያብሩ"; +"learn_to_share_tool.light_up_the_tips.message" = "ጠቃሚ ምክር ሲበራ ያንን ስልጠና ጨርሰዋል። በእያንዳንዱ ውይይት ውስጥ እያንዳንዱን ጠቃሚ ምክር መጠቀም አያስፈልግዎትም."; +"lesson_evaluation.title" = "ገምግም"; +"lesson_evaluation.wasThisHelpful" = "ይህ ትምህርት ለእርስዎ ጠቃሚ ነበር?"; +"lesson_evaluation.shareFaith" = "መንፈሳዊ ውይይት ለመጀመር ምን ያህል ዝግጁ ነዎት?"; +"lesson_evaluation.sendButtonTitle" = "አስተያይት ላክ"; +"onboardingTutorial.0.title" = "ለእያንዳንዱ ውይይት ዝግጁ"; +"onboardingTutorial.0.videoLink.title" = "ቪዲዮ ተመልከት"; +"onboardingTutorial.1.title" = "ከማንም ጋር ስለ እግዚአብሔር ተነጋገሩ"; +"onboardingTutorial.1.message" = "GodTools ስለ እግዚአብሔር ትርጉም ያለው ንግግሮች እንዲያደርጉ ያግዝዎታል።"; +"onboardingTutorial.2.title" = "አስፈላጊ ለሆኑ ቅፅበቶች ይዘጋጁ"; +"onboardingTutorial.2.message" = "ስለ እምነት ውይይቶችን እንዴት ከህይወቶ ጋር በማዋሀድ በቀላሉ ማውራት እንደሚችሉ ይወቁ።"; +"onboardingTutorial.3.title" = "አንድ ሰው ኢየሱስን እንዲያገኝ እርዱት"; +"onboardingTutorial.3.message" = "GodTools ወንጌልን ግልጽ እና ተዛማጅ በሆኑ መንገዶች ለማስተላለፍ ሚያግዝዎ መመሪያዎ ነው።"; /* This is a category. The content will include training of how to share your faith. */ "tool_category_training" = "ስልጠና"; + +/* This is an invitation for a user to open a tool */ +"open" = "ክፈት"; + +/* A user will be selecting a heart to indicated that it is one of their favorite tools. */ +"add_to_favorites" = "ወደ ምርጫዎች ጨምር"; + +/* A user will be selecting this option to remove the tool from their Favorites screen */ +"remove_from_favorites" = "ከምርጫዎችህ አውጣ"; + +/* This is a loading screen */ +"loading_unfavorited_tool" = "ቡክሌቱ እየወረደ ነው። ለወደፊቱ ይህን ቡክሌት ያለ በይነመረብ ግንኙነት ለማግኘት የኔ ምርጫ ውስጥ አስገባው።"; + +/* This is a loading screen */ +"loading_favorited_tool" = "ለዚህ ቡክሌት በጣም የቅርብ ጊዜውን ትርጉም በማውረድ ላይ።"; + +/* Adds a question mark */ +"remove_from_favorites_title" = "ከምርጫዎችህ አውጣ? "; + +/* No translation needed for %@ - just use it as it is. It will be substituted with the name of the tool. */ +"remove_from_favorites_message" = "እርግጠኛ ነዎት %@ን ከምርጫዎ ማስወገድ ይፈልጋሉ? "; + +/* Gospel indicates the gospel of Jesus Christ */ +"tool_category_gospel" = "የወንጌል ግብዣ"; + +/* Describes a format of content - like a newspaper article. */ +"tool_category_articles" = "መጣጥፎች"; + +"articles.downloadArticlesButton.title.retryDownload" = "ለማውረድ እንደገና ይሞክሩ"; + +/* This is to let users know which version of the app is installed on their phone */ +"menu_version" = "ሥሪት"; "language_onboard" = "ከ 90 በላይ ቋንቋዎች"; /* This is to indicate to the user that there are no additional tools or resources available for download because they have already downloaded everything. */ "no_more_tools" = "ሁሉንም መሳሪያዎች አዉርደዋል"; /* Each offering inside the app listed on the Home Screen is considered a "Tool". You might look a few phrases down to phrase #31884901 to see how the word Tool was previously translated. */ -"my_tools" = "የኔ መሳሪያዎች"; +"my_tools" = "የኔ ምርጫ"; /* The user will click this toggle switch to go find a library of more offerings or "Tools". */ -"find_tools" = "መሳሪያዎችን ፈልግ"; +"find_tools" = "ሁሉም ቡክሌቶች"; + +// Navigation Bar +"navigationBar.navigationItem.skip" = "እለፍ"; // Onboarding "add_new_tools" = "አዲስ መሳሪያን ይጨምሩ"; // Menu "menu_general" = "አጠቃላይ"; +"menu_account" = "መለያ"; "menu_share" = "ያጋሩ"; "menu_legal" = "ሕጋዊ"; -"language_settings" = "የቋንቋ ማስተካከያ"; +"language_settings" = "የቋንቋ ቅንብሮች"; +"menu.tutorial" = "አጋዥ ስልጠና"; +"menu.my_account" = "የኔ መለያ"; + // Login "login" = "ግባ"; "logout" = "ዉጣ"; @@ -36,11 +304,20 @@ "help" = "እገዛ"; "contact_us" = "አግኙን"; "share_god_tools" = "GodTools መተግበሪያን አጋራ"; -"share_a_story_with_us" = "ታሪክዎን ከእኛ ጋር ይጋሩ"; -"terms_of_use" = "የአጠቃቀም መመሪያ"; -"privacy_policy" = "የግላዊነት ደምቦች"; +"share_a_story_with_us" = "ታሪክ አካፍሉን"; +"terms_of_use" = "የአጠቃቀም ውል"; +"privacy_policy" = "የግላዊነት መመሪያ"; +"copyright_info" = "የቅጂ መብት"; +"share_god_tools_share_sheet_text" = "የ GodTools መተግበሪያን ያውርዱ: https://godtoolsapp.com \n\n ሁሉንም + +የመተግበሪያውን ይዘት ይህን መስፈንጠሪያ በመንካት ማግኘት ይቻላል https://knowgod.com/"; + +"total_views" = "%d ታይቷል"; +"total_languages" = "%d ቋንቋዎች"; + // Language Settings "primary_language" = "ዋና ቋንቋ"; +"select_primary_language" = "ዋና ቋንቋን ይምረጡ"; "parallel_language" = "ሁለት ቋንቋ"; "select_parallel_language" = "ሁለት ቋንቋን ይምረጡ"; "share_god_tools_native_language" = "GodTools'ን ለሰዎች በራሳቸዉ ቋንቋ አጋራ"; @@ -49,39 +326,80 @@ // Settings "settings" = "ድባቦች"; +// Alert & Info Controllers +"download_error" = "የማውረድ ስህተት"; +"no_internet_title" = "ምንም የበይነመረብ ግንኙነት የለም።"; +"no_internet" = "እባክዎ የበይነመረብ ግንኙነትዎን ያረጋግጡ እና እንደገና ይሞክሩ።"; + "the_tool_you_requested_is_loading" = "የጠየቁት መተግበርያ በሂደት ላይ ነዉ..."; // Actions "yes" = "አዎ"; "no" = "አይደለም"; +// Tract - Sharing +"tract_share_message" ="ዛሬ ስንመለከተዉ የነበረዉ መተግብሪያ(App) በዚህ መስፈንጠሪያ በኩል ማግኘት ይችላሉ: %@ ይህን መተግበሪያ ዌብ ላይ ለመጠቀም መስፈንጠሪያ: https://godtoolsapp.com\n\n ተጨማሪ እንነጋገር !"; + // Buttons "download" = "ያዉርዱ"; +"dismiss" = "አጥፋ"; "done" = "ሆኗል"; "remove" = "ያጥፉ"; +"clear" = "አጥፋ"; /* Please be aware that the word Tool has been used in other places in this project. Check for consistency. */ "toolinfo_opentool" = "ክፈት"; +// Alerts +"error" = "ስህተት"; "OK" = "እሺ"; "cancel" = "ውጣ"; +"required_field_missing" = "\"%@\" አስፈላጊ ገጽታ ነው"; + +"onboardingTutorial.getStartedButton.title" = "እንጀምር"; + // About "general_about_1" = "GodTools እምነትዎን በቀላል እና በግልጽ መንገድ እንዲያካፍሉ ይረዳዎታል"; +// Tutorial +"tutorial.tutorialItem.0.title" = "ተመልከት"; "general_about_2" = "ይህ መተግበርያ ሰዎች ከእግዚአብሄር ጋር በልጁ በኢየሱስ ክርስቶስ በኩል ግንኙነት እንዲኖራቸዉ በድፍረት እንዲያግዙ ይረዳዎታል። ከዚህ በፊት እምነትዎን አካፍለዉ የማያዉቁ ቢሆኑ እና የሌላ ቋንቋ ተናጋሪ ሰዉ ቢገጥምዎ ይህ መተግበርያ በደንብ ያግዛዎታል።"; "general_about_3" = "ከብዙ አይነት መተግበሪያ(መሳሪያ) የሚሆኖትን ለተመልካቹም የሚመቸዉን ይምረጡ።"; "general_about_4" = "ከሁሉም መተግበሪያ(መሳሪያ) መጨረሻ ላይ ወደ ምንጩ የሚወስድ ጠቋሚ (ሊንክ) ይገኛል።"; "general_about_5" = "አዳዲስ መተግበሪያዎችን(አገልግሎቶችን) እና ቋንቋን አንዴ ለማዉረድ ኢንተርኔት ኮኔክሽን ያስፈልጋል። አንድ ጊዜ ከወረደ በዃላ ግን እንደዚዉ ይሰራል።"; "general_about_6" = "ጥያቄዎች? ጥቆማዎች? በዚህ አድራሻ አግኙን support@godtoolsapp.com"; "general_about_7" = "እዚህ ላይ ያሉትን አገልግሎቶችን ኦንላይን knowgod.com ላይ ማግኘት ይችላሉ። የራስዎም ሳይት ላይ መጠቀም ይችላሉ።"; +"tutorial.tutorialItem.3.title" = "ይህ አጋዥ ስልጠና በማውጫው ውስጥ እንደገና ለማየት ይገኛል።"; "general_about_8" = "ለተጨማሪ መረጃ godtoolsapp.com ይመልከቱ"; +"tutorial.continueButton.title.continue" = "ቀጥል"; "general_about_9" = "እምነትዎን ለማካፈል ሲነሱ የሰዉን ልብ የሚቀይረዉ እግዚአብሄር መሆኑን ያስታዉሱ። ሁሉንም ሰዉ በትክክል መዉደድ እና ማዳመጥ ያስፈልጋል።"; +"tutorial.continueButton.title.startUsingGodTools" = "GodTools መጠቀም ይጀምር"; + "general_about_10" = "ለእያንዳንዱ እንዴት እንድትመልሱ እንደሚገባችሁ ታውቁ ዘንድ ንግግራችሁ ሁልጊዜ፥ በጨው እንደ ተቀመመ፥ በጸጋ ይሁን። ቆላስይስ 4:6"; +// OpenTutorial +"openTutorial.showTutorialLabel.text" = "አጋዥ ስልጠና አሳየኝ"; // Downloads "Download in progress" = "በሂደት ላይ አውርድሂደት ላይ አውርድ"; +"openTutorial.openTutorialButton.title" = "አጋዥ ስልጠናን ክፈት"; + // Navigation - this is an abbreviation for the word "Previous" meaning go back to the card that was previous to this one. "card_status1" = "ቀዳሚ።"; +// Account +"account.navTitle" = "የግል ማህደሬ"; "card_status2" = "ቀጣይ"; +"account.activity.title" = "እንቅስቃሴ"; + // Language Name Strings "language_name_fa" = "ፐርሽያን (ፋርሲ)"; +// Account Activity +"accountActivity.globalAnalytics.header.title" = "ዓለም አቀፋዊ እንቅስቃሴ"; +"accountActivity.globalAnalytics.users.title" = "ልዩ ተጠቃሚዎች"; +"onboardingWelcome.titleLabel.tagline" = "እምነትህን እንድታካፍል መርዳት"; +"accountActivity.globalAnalytics.gospelPresentation.title" = "የወንጌል ግብዣዎች"; +"accountActivity.globalAnalytics.launches.title" = "ክፍለ ጊዜዎች"; +// OnboardingTutorial +"onboardingTutorial.aboutAppItem.0.title" = "ለሌሎች አካፍል"; +"accountActivity.globalAnalytics.countries.title" = "ሀገሮች"; + +"onboardingTutorial.showMoreButton.title" = "ተጨማሪ አሳየኝ"; diff --git a/godtools/am.lproj/Localizable.stringsdict b/godtools/am.lproj/Localizable.stringsdict index fd8fe6a23e..21edba576d 100644 --- a/godtools/am.lproj/Localizable.stringsdict +++ b/godtools/am.lproj/Localizable.stringsdict @@ -13,9 +13,9 @@ NSStringFormatValueTypeKey d one -Tool open +የተከፈቱ ቡክሌቶች other -Tool opens +የተከፈቱ ቡክሌቶች account.activity.lessonCompletions @@ -29,9 +29,9 @@ NSStringFormatValueTypeKey d one -Lesson completion +የትምህርት ማጠናቀቂያዎች other -Lesson completions +የትምህርት ማጠናቀቂያዎች account.activity.screenShares @@ -45,9 +45,9 @@ NSStringFormatValueTypeKey d one -Screen share +ስክሪን ማጋራቶች other -Screen shares +ስክሪን ማጋራቶች account.activity.linksShared @@ -61,9 +61,9 @@ NSStringFormatValueTypeKey d one -Link share +ማስፈንጠሪያ ማጋራት other -Link shares +ማስፈንጠሪያ ማጋራቶች account.activity.languagesUsed @@ -77,9 +77,9 @@ NSStringFormatValueTypeKey d one -Language used +የተጠቀሙዋቸው ቋንቋዎች other -Languages used +የተጠቀሙዋቸው ቋንቋዎች account.activity.sessions @@ -93,9 +93,9 @@ NSStringFormatValueTypeKey d one -Session +ክፍለ ጊዜዎች other -Sessions +ክፍለ ጊዜዎች tools.filter.toolsAvailable @@ -109,9 +109,9 @@ NSStringFormatValueTypeKey d one -%d Tool available +%d ቡክሌቶች አሉ other -%d Tools available +%d ቡክሌቶች አሉ languageSettings.appLanguage.numberAvailable @@ -125,9 +125,9 @@ NSStringFormatValueTypeKey d one -1 Language available +%d ቋንቋ ይገኛል other -%d Languages available +%d ቋንቋ ይገኛል badges.imagesShared @@ -141,9 +141,9 @@ NSStringFormatValueTypeKey d one -Shared 1 image +የተጋሩ %d ምስሎች other -Shared %d images +የተጋሩ %d ምስሎች badges.articlesOpened @@ -157,9 +157,9 @@ NSStringFormatValueTypeKey d one -Read 1 article +1 ጽሑፍ ያንብቡ other -Read %d articles +%d ጽሑፎችን ያንብቡ badges.lessonsCompleted @@ -173,9 +173,9 @@ NSStringFormatValueTypeKey d one -Completed 1 lesson +የተጠናቀቁ 1 ትምህርቶች other -Completed %d lessons +የተጠናቀቁ %d ትምህርቶች badges.toolsOpened @@ -189,9 +189,9 @@ NSStringFormatValueTypeKey d one -Opened 1 tool +1 መሳሪያዎች ተከፍተዋል other -Opened %d tools +%d መሳሪያዎች ተከፍተዋል badges.tipsCompleted @@ -205,9 +205,9 @@ NSStringFormatValueTypeKey d one -Completed 1 training tip +የተጠናቀቁ 1 የሥልጠና ምክሮች other -Completed %d training tips +የተጠናቀቁ %d የሥልጠና ምክሮች diff --git a/godtoolsTests/App/Features/AppLanguage/Data-DomainInterface/GetLanguageSettingsInterfaceStringsRepositoryTests.swift b/godtoolsTests/App/Features/AppLanguage/Data-DomainInterface/GetLanguageSettingsInterfaceStringsRepositoryTests.swift index 0edf61e0e4..ad6024d2a7 100644 --- a/godtoolsTests/App/Features/AppLanguage/Data-DomainInterface/GetLanguageSettingsInterfaceStringsRepositoryTests.swift +++ b/godtoolsTests/App/Features/AppLanguage/Data-DomainInterface/GetLanguageSettingsInterfaceStringsRepositoryTests.swift @@ -16,37 +16,92 @@ class GetLanguageSettingsInterfaceStringsRepositoryTests: QuickSpec { override class func spec() { + var cancellables: Set = Set() + + let testsDiContainer = TestsDiContainer() + + let testsRealmDatabase: RealmDatabase = testsDiContainer.dataLayer.getSharedRealmDatabase() + + let appLanguages: [AppLanguageCodable] = [ + AppLanguageCodable(languageCode: "en", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "es", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hans"), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hant"), + AppLanguageCodable(languageCode: "lv", languageDirection: .leftToRight, languageScriptCode: nil) + ] + + let numberOfTestAppLanguages: Int = appLanguages.count + + let mockAppLanguagesSync: AppLanguagesRepositorySyncInterface = MockAppLanguagesRepositorySync( + realmDatabase: testsRealmDatabase, + appLanguages: appLanguages + ) + + let appLanguagesRepository: AppLanguagesRepository = testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository( + realmDatabase: testsRealmDatabase, + sync: mockAppLanguagesSync + ) + + let localizableStrings: [MockLocalizationServices.LocaleId: [MockLocalizationServices.StringKey: String]] = [ + LanguageCodeDomainModel.english.value: [ + LocalizableStringKeys.languageSettingsNavTitle.key: "Language settings", + LocalizableStringKeys.languageSettingsAppInterfaceTitle.key: "App interface language", + LocalizableStringKeys.languageSettingsAppInterfaceMessage.key: "Set the language you'd like the whole app to be displayed in.", + LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineTitle.key: "Tool languages available offline", + LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineMessage.key: "Download all the tools in a language to make them available even if you're out of WiFi or cell service. Set the tool language via the options button within a tool.", + LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineEditDownloadedLanguagesButtonTitle.key: "Edit downloaded languages", + LocalizableStringDictKeys.languageSettingsAppLanguageNumberAvailable.key: "%d Languages available" + ], + LanguageCodeDomainModel.spanish.value: [ + LocalizableStringKeys.languageSettingsNavTitle.key: "Ajustes de idioma", + LocalizableStringKeys.languageSettingsAppInterfaceTitle.key: "Idioma de la interfaz de la aplicación", + LocalizableStringKeys.languageSettingsAppInterfaceMessage.key: "Establece el idioma en el que deseas que se muestre toda la aplicación.", + LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineTitle.key: "Idiomas de herramientas disponibles sin conexión", + LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineMessage.key: "Descarga todas las herramientas en un idioma para que estén disponibles incluso si no tienes WiFi o servicio móvil. Establece el idioma de la herramienta mediante el botón de opciones dentro de una herramienta.", + LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineEditDownloadedLanguagesButtonTitle.key: "Editar idiomas descargados", + LocalizableStringDictKeys.languageSettingsAppLanguageNumberAvailable.key: "%d Idiomas disponibles" + ] + ] + + let languageNames: [MockLocaleLanguageName.LanguageCode: [MockLocaleLanguageName.TranslateInLocaleId: MockLocaleLanguageName.LanguageName]] = [ + LanguageCodeDomainModel.english.rawValue: [ + LanguageCodeDomainModel.english.rawValue: "English", + LanguageCodeDomainModel.portuguese.rawValue: "Inglês", + LanguageCodeDomainModel.spanish.rawValue: "Inglés", + LanguageCodeDomainModel.russian.rawValue: "Английский" + ], + LanguageCodeDomainModel.french.rawValue: [ + LanguageCodeDomainModel.czech.rawValue: "francouzština", + LanguageCodeDomainModel.english.rawValue: "French", + LanguageCodeDomainModel.portuguese.rawValue: "Francês", + LanguageCodeDomainModel.spanish.rawValue: "Francés", + LanguageCodeDomainModel.russian.rawValue: "Французский" + ], + LanguageCodeDomainModel.spanish.rawValue: [ + LanguageCodeDomainModel.english.rawValue: "Spanish", + LanguageCodeDomainModel.portuguese.rawValue: "Espanhol", + LanguageCodeDomainModel.spanish.rawValue: "Español", + LanguageCodeDomainModel.russian.rawValue: "испанский" + ] + ] + + let getTranslatedLanguageName = GetTranslatedLanguageName( + localizationLanguageNameRepository: MockLocalizationLanguageNameRepository(localizationServices: MockLocalizationServices(localizableStrings: localizableStrings)), + localeLanguageName: MockLocaleLanguageName(languageNames: languageNames), + localeRegionName: MockLocaleLanguageRegionName(regionNames: [:]), + localeScriptName: MockLocaleLanguageScriptName(scriptNames: [:]) + ) + + let getLanguageSettingsInterfaceStringsRepository = GetLanguageSettingsInterfaceStringsRepository( + localizationServices: MockLocalizationServices(localizableStrings: localizableStrings), + getTranslatedLanguageName: getTranslatedLanguageName, + appLanguagesRepository: appLanguagesRepository + ) + describe("User is viewing the language settings.") { context("When the app language is switched from English to Spanish.") { - - let localizableStrings: [MockLocalizationServices.LocaleId: [MockLocalizationServices.StringKey: String]] = [ - LanguageCodeDomainModel.english.value: [ - LocalizableStringKeys.languageSettingsNavTitle.key: "Language settings", - LocalizableStringKeys.languageSettingsAppInterfaceTitle.key: "App interface language", - LocalizableStringKeys.languageSettingsAppInterfaceMessage.key: "Set the language you'd like the whole app to be displayed in.", - LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineTitle.key: "Tool languages available offline", - LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineMessage.key: "Download all the tools in a language to make them available even if you're out of WiFi or cell service. Set the tool language via the options button within a tool.", - LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineEditDownloadedLanguagesButtonTitle.key: "Edit downloaded languages" - ], - LanguageCodeDomainModel.spanish.value: [ - LocalizableStringKeys.languageSettingsNavTitle.key: "Ajustes de idioma", - LocalizableStringKeys.languageSettingsAppInterfaceTitle.key: "Idioma de la interfaz de la aplicación", - LocalizableStringKeys.languageSettingsAppInterfaceMessage.key: "Establece el idioma en el que deseas que se muestre toda la aplicación.", - LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineTitle.key: "Idiomas de herramientas disponibles sin conexión", - LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineMessage.key: "Descarga todas las herramientas en un idioma para que estén disponibles incluso si no tienes WiFi o servicio móvil. Establece el idioma de la herramienta mediante el botón de opciones dentro de una herramienta.", - LocalizableStringKeys.languageSettingsToolLanguagesAvailableOfflineEditDownloadedLanguagesButtonTitle.key: "Editar idiomas descargados" - ] - ] - - let testsDiContainer = TestsDiContainer() - - let getLanguageSettingsInterfaceStringsRepository = GetLanguageSettingsInterfaceStringsRepository( - localizationServices: MockLocalizationServices(localizableStrings: localizableStrings), - translatedLanguageNameRepository: testsDiContainer.dataLayer.getTranslatedLanguageNameRepository(), - appLanguagesRepository: testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository() - ) - + it("The interface strings should be translated into Spanish.") { let appLanguagePublisher: CurrentValueSubject = CurrentValueSubject(LanguageCodeDomainModel.english.value) @@ -59,7 +114,7 @@ class GetLanguageSettingsInterfaceStringsRepositoryTests: QuickSpec { waitUntil { done in - _ = appLanguagePublisher + appLanguagePublisher .flatMap({ (appLanguage: AppLanguageDomainModel) -> AnyPublisher in return getLanguageSettingsInterfaceStringsRepository @@ -91,6 +146,7 @@ class GetLanguageSettingsInterfaceStringsRepositoryTests: QuickSpec { appLanguagePublisher.send(LanguageCodeDomainModel.spanish.rawValue) } } + .store(in: &cancellables) } expect(englishInterfaceStringsRef?.navTitle).to(equal("Language settings")) @@ -111,22 +167,14 @@ class GetLanguageSettingsInterfaceStringsRepositoryTests: QuickSpec { context("When the app language is English.") { - let testsDiContainer = TestsDiContainer() - - let getLanguageSettingsInterfaceStringsRepository = GetLanguageSettingsInterfaceStringsRepository( - localizationServices: MockLocalizationServices(localizableStrings: [:]), - translatedLanguageNameRepository: testsDiContainer.dataLayer.getTranslatedLanguageNameRepository(), - appLanguagesRepository: testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository() - ) - it("I expect the choose app language button title to display my app language English translated in English.") { - + var interfaceStringsRef: LanguageSettingsInterfaceStringsDomainModel? var sinkCompleted: Bool = false waitUntil { done in - _ = getLanguageSettingsInterfaceStringsRepository + getLanguageSettingsInterfaceStringsRepository .getStringsPublisher(translateInAppLanguage: "en") .sink { (interfaceStrings: LanguageSettingsInterfaceStringsDomainModel) in @@ -140,7 +188,8 @@ class GetLanguageSettingsInterfaceStringsRepositoryTests: QuickSpec { done() - } + } + .store(in: &cancellables) } expect(interfaceStringsRef?.chooseAppLanguageButtonTitle).to(equal("English")) @@ -149,22 +198,14 @@ class GetLanguageSettingsInterfaceStringsRepositoryTests: QuickSpec { context("When the app language is Spanish.") { - let testsDiContainer = TestsDiContainer() - - let getLanguageSettingsInterfaceStringsRepository = GetLanguageSettingsInterfaceStringsRepository( - localizationServices: MockLocalizationServices(localizableStrings: [:]), - translatedLanguageNameRepository: testsDiContainer.dataLayer.getTranslatedLanguageNameRepository(), - appLanguagesRepository: testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository() - ) - it("I expect the choose app language button title to display my app language Spanish translated in Spanish.") { - + var interfaceStringsRef: LanguageSettingsInterfaceStringsDomainModel? var sinkCompleted: Bool = false waitUntil { done in - _ = getLanguageSettingsInterfaceStringsRepository + getLanguageSettingsInterfaceStringsRepository .getStringsPublisher(translateInAppLanguage: "es") .sink { (interfaceStrings: LanguageSettingsInterfaceStringsDomainModel) in @@ -178,36 +219,25 @@ class GetLanguageSettingsInterfaceStringsRepositoryTests: QuickSpec { done() - } + } + .store(in: &cancellables) } - expect(interfaceStringsRef?.chooseAppLanguageButtonTitle).to(equal("español")) + expect(interfaceStringsRef?.chooseAppLanguageButtonTitle).to(equal("Español")) } } context("When my app language is English.") { - - let testsDiContainer = TestsDiContainer() - - let getLanguageSettingsInterfaceStringsRepository = GetLanguageSettingsInterfaceStringsRepository( - localizationServices: MockLocalizationServices(localizableStrings: [:]), - translatedLanguageNameRepository: testsDiContainer.dataLayer.getTranslatedLanguageNameRepository(), - appLanguagesRepository: testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository() - ) - + it("I expect to see the number of app languages available translated in my app language English.") { - - // TODO: Need to complete this test. I would like to include the count of app languages in this test. However, AppLanguagesRepository is pointing to - // a hard-coded cache of app languages. I should see about storing app languages in Realm and mocking this in the test. ~Levi - - /* + var interfaceStringsRef: LanguageSettingsInterfaceStringsDomainModel? var sinkCompleted: Bool = false waitUntil { done in - _ = getLanguageSettingsInterfaceStringsRepository - .getStringsPublisher(translateInAppLanguage: "es") + getLanguageSettingsInterfaceStringsRepository + .getStringsPublisher(translateInAppLanguage: "en") .sink { (interfaceStrings: LanguageSettingsInterfaceStringsDomainModel) in guard !sinkCompleted else { @@ -217,13 +247,15 @@ class GetLanguageSettingsInterfaceStringsRepositoryTests: QuickSpec { sinkCompleted = true interfaceStringsRef = interfaceStrings - + done() - } - }*/ - - //expect(englishInterfaceStringsRef?.numberOfAppLanguagesAvailable).to(equal("27")) + } + .store(in: &cancellables) + } + + expect(interfaceStringsRef?.numberOfAppLanguagesAvailable).to(contain("\(numberOfTestAppLanguages)")) + expect(interfaceStringsRef?.numberOfAppLanguagesAvailable).to(equal("\(numberOfTestAppLanguages) Languages available")) } } } diff --git a/godtoolsTests/App/Features/AppLanguage/Data-DomainInterface/StoreInitialAppLanguageTests.swift b/godtoolsTests/App/Features/AppLanguage/Data-DomainInterface/StoreInitialAppLanguageTests.swift index 8cfa8b9163..d50cb5c92f 100644 --- a/godtoolsTests/App/Features/AppLanguage/Data-DomainInterface/StoreInitialAppLanguageTests.swift +++ b/godtoolsTests/App/Features/AppLanguage/Data-DomainInterface/StoreInitialAppLanguageTests.swift @@ -16,16 +16,43 @@ class StoreInitialAppLanguageTests: QuickSpec { override class func spec() { + var cancellables: Set = Set() + describe("App is launched.") { context("When no app language is currently set and my device language is english.") { - + let testsDiContainer = TestsDiContainer() + let testsRealmDatabase: RealmDatabase = testsDiContainer.dataLayer.getSharedRealmDatabase() + + let appLanguages: [AppLanguageCodable] = [ + AppLanguageCodable(languageCode: "ar", languageDirection: .rightToLeft, languageScriptCode: nil), + AppLanguageCodable(languageCode: "en", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "es", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hans"), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hant"), + AppLanguageCodable(languageCode: "lv", languageDirection: .leftToRight, languageScriptCode: nil) + ] + + let mockAppLanguagesSync: AppLanguagesRepositorySyncInterface = MockAppLanguagesRepositorySync( + realmDatabase: testsRealmDatabase, + appLanguages: appLanguages + ) + + let userAppLanguageCache: RealmUserAppLanguageCache = RealmUserAppLanguageCache(realmDatabase: testsRealmDatabase) + + _ = userAppLanguageCache.deleteLanguage() + + let appLanguagesRepository: AppLanguagesRepository = testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository( + realmDatabase: testsRealmDatabase, + sync: mockAppLanguagesSync + ) + let storeInitialAppLanguage = StoreInitialAppLanguage( deviceSystemLanguage: MockDeviceSystemLanguage.getMockEnglishDevice(), - userAppLanguageRepository: testsDiContainer.feature.appLanguage.dataLayer.getUserAppLanguageRepository(), - appLanguagesRepository: testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository() + userAppLanguageRepository: UserAppLanguageRepository(cache: userAppLanguageCache), + appLanguagesRepository: appLanguagesRepository ) it("I expect my app language to be the device language english since english is a supported app language.") { @@ -34,9 +61,9 @@ class StoreInitialAppLanguageTests: QuickSpec { var sinkCompleted: Bool = false - waitUntil { done in + waitUntil(timeout: .seconds(15)) { done in - _ = storeInitialAppLanguage + storeInitialAppLanguage .storeInitialAppLanguagePublisher() .sink { (result: AppLanguageDomainModel) in @@ -50,6 +77,7 @@ class StoreInitialAppLanguageTests: QuickSpec { done() } + .store(in: &cancellables) } expect(resultRef).to(equal("en")) @@ -57,13 +85,38 @@ class StoreInitialAppLanguageTests: QuickSpec { } context("When no app language is currently set and my device language is arabic.") { - + let testsDiContainer = TestsDiContainer() + let testsRealmDatabase: RealmDatabase = testsDiContainer.dataLayer.getSharedRealmDatabase() + + let appLanguages: [AppLanguageCodable] = [ + AppLanguageCodable(languageCode: "ar", languageDirection: .rightToLeft, languageScriptCode: nil), + AppLanguageCodable(languageCode: "en", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "es", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hans"), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hant"), + AppLanguageCodable(languageCode: "lv", languageDirection: .leftToRight, languageScriptCode: nil) + ] + + let mockAppLanguagesSync: AppLanguagesRepositorySyncInterface = MockAppLanguagesRepositorySync( + realmDatabase: testsRealmDatabase, + appLanguages: appLanguages + ) + + let userAppLanguageCache: RealmUserAppLanguageCache = RealmUserAppLanguageCache(realmDatabase: testsRealmDatabase) + + _ = userAppLanguageCache.deleteLanguage() + + let appLanguagesRepository: AppLanguagesRepository = testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository( + realmDatabase: testsRealmDatabase, + sync: mockAppLanguagesSync + ) + let storeInitialAppLanguage = StoreInitialAppLanguage( deviceSystemLanguage: MockDeviceSystemLanguage(deviceLocale: Locale(identifier: "ar")), - userAppLanguageRepository: testsDiContainer.feature.appLanguage.dataLayer.getUserAppLanguageRepository(), - appLanguagesRepository: testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository() + userAppLanguageRepository: UserAppLanguageRepository(cache: userAppLanguageCache), + appLanguagesRepository: appLanguagesRepository ) it("I expect my app language to be the device language arabic since arabic is a supported app language.") { @@ -74,7 +127,7 @@ class StoreInitialAppLanguageTests: QuickSpec { waitUntil { done in - _ = storeInitialAppLanguage + storeInitialAppLanguage .storeInitialAppLanguagePublisher() .sink { (result: AppLanguageDomainModel) in @@ -88,6 +141,7 @@ class StoreInitialAppLanguageTests: QuickSpec { done() } + .store(in: &cancellables) } expect(resultRef).to(equal("ar")) @@ -95,33 +149,50 @@ class StoreInitialAppLanguageTests: QuickSpec { } context("When my app language is set to spanish and my device language is arabic.") { - + let testsDiContainer = TestsDiContainer() - let userAppLanguageRepository: UserAppLanguageRepository = testsDiContainer.feature.appLanguage.dataLayer.getUserAppLanguageRepository() + let testsRealmDatabase: RealmDatabase = testsDiContainer.dataLayer.getSharedRealmDatabase() + + let appLanguages: [AppLanguageCodable] = [ + AppLanguageCodable(languageCode: "ar", languageDirection: .rightToLeft, languageScriptCode: nil), + AppLanguageCodable(languageCode: "en", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "es", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hans"), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hant"), + AppLanguageCodable(languageCode: "lv", languageDirection: .leftToRight, languageScriptCode: nil) + ] + + let mockAppLanguagesSync: AppLanguagesRepositorySyncInterface = MockAppLanguagesRepositorySync( + realmDatabase: testsRealmDatabase, + appLanguages: appLanguages + ) + + let userAppLanguageCache: RealmUserAppLanguageCache = RealmUserAppLanguageCache(realmDatabase: testsRealmDatabase) + + userAppLanguageCache.storeLanguage(languageId: "es") + + let appLanguagesRepository: AppLanguagesRepository = testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository( + realmDatabase: testsRealmDatabase, + sync: mockAppLanguagesSync + ) let storeInitialAppLanguage = StoreInitialAppLanguage( deviceSystemLanguage: MockDeviceSystemLanguage(deviceLocale: Locale(identifier: "ar")), - userAppLanguageRepository: userAppLanguageRepository, - appLanguagesRepository: testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository() + userAppLanguageRepository: UserAppLanguageRepository(cache: userAppLanguageCache), + appLanguagesRepository: appLanguagesRepository ) it("I expect my app language to stay in spanish.") { - + var resultRef: AppLanguageDomainModel? var sinkCompleted: Bool = false waitUntil { done in - _ = userAppLanguageRepository - .storeLanguagePublisher(languageId: "es") - .flatMap({ (didStore: Bool) -> AnyPublisher in - - return storeInitialAppLanguage - .storeInitialAppLanguagePublisher() - .eraseToAnyPublisher() - }) + storeInitialAppLanguage + .storeInitialAppLanguagePublisher() .sink { (result: AppLanguageDomainModel) in guard !sinkCompleted else { @@ -134,6 +205,7 @@ class StoreInitialAppLanguageTests: QuickSpec { done() } + .store(in: &cancellables) } expect(resultRef).to(equal("es")) @@ -144,10 +216,35 @@ class StoreInitialAppLanguageTests: QuickSpec { let testsDiContainer = TestsDiContainer() + let testsRealmDatabase: RealmDatabase = testsDiContainer.dataLayer.getSharedRealmDatabase() + + let appLanguages: [AppLanguageCodable] = [ + AppLanguageCodable(languageCode: "ar", languageDirection: .rightToLeft, languageScriptCode: nil), + AppLanguageCodable(languageCode: "en", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "es", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hans"), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hant"), + AppLanguageCodable(languageCode: "lv", languageDirection: .leftToRight, languageScriptCode: nil) + ] + + let mockAppLanguagesSync: AppLanguagesRepositorySyncInterface = MockAppLanguagesRepositorySync( + realmDatabase: testsRealmDatabase, + appLanguages: appLanguages + ) + + let userAppLanguageCache: RealmUserAppLanguageCache = RealmUserAppLanguageCache(realmDatabase: testsRealmDatabase) + + _ = userAppLanguageCache.deleteLanguage() + + let appLanguagesRepository: AppLanguagesRepository = testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository( + realmDatabase: testsRealmDatabase, + sync: mockAppLanguagesSync + ) + let storeInitialAppLanguage = StoreInitialAppLanguage( deviceSystemLanguage: MockDeviceSystemLanguage(deviceLocale: Locale(identifier: "cs")), - userAppLanguageRepository: testsDiContainer.feature.appLanguage.dataLayer.getUserAppLanguageRepository(), - appLanguagesRepository: testsDiContainer.feature.appLanguage.dataLayer.getAppLanguagesRepository() + userAppLanguageRepository: UserAppLanguageRepository(cache: userAppLanguageCache), + appLanguagesRepository: appLanguagesRepository ) it("I expect my app language to be english since Czech is not a supported app language.") { @@ -158,7 +255,7 @@ class StoreInitialAppLanguageTests: QuickSpec { waitUntil { done in - _ = storeInitialAppLanguage + storeInitialAppLanguage .storeInitialAppLanguagePublisher() .sink { (result: AppLanguageDomainModel) in @@ -172,6 +269,7 @@ class StoreInitialAppLanguageTests: QuickSpec { done() } + .store(in: &cancellables) } expect(resultRef).to(equal("en")) diff --git a/godtoolsTests/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/MockAppLanguagesRepositorySync.swift b/godtoolsTests/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/MockAppLanguagesRepositorySync.swift new file mode 100644 index 0000000000..bd713949ac --- /dev/null +++ b/godtoolsTests/App/Features/AppLanguage/Data/AppLanguagesRepository/Sync/MockAppLanguagesRepositorySync.swift @@ -0,0 +1,72 @@ +// +// MockAppLanguagesRepositorySync.swift +// godtoolsTests +// +// Created by Levi Eggert on 5/7/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +@testable import godtools +import Combine +import RealmSwift + +class MockAppLanguagesRepositorySync: AppLanguagesRepositorySyncInterface { + + private let realmDatabase: RealmDatabase + private let appLanguages: [AppLanguageCodable] + + init(realmDatabase: RealmDatabase, appLanguages: [AppLanguageCodable]) { + + self.realmDatabase = realmDatabase + self.appLanguages = appLanguages + + addAppLanguagesToRealm(appLanguages: appLanguages) + } + + func syncPublisher() -> AnyPublisher { + + guard appLanguages.isEmpty else { + return Just(Void()) + .eraseToAnyPublisher() + } + + addAppLanguagesToRealm(appLanguages: appLanguages) + + return Just(()) + .eraseToAnyPublisher() + } + + private func addAppLanguagesToRealm(appLanguages: [AppLanguageCodable]) { + + _ = realmDatabase.writeObjects(realm: realmDatabase.openRealm()) { (realm: Realm) in + + let realmLanguages: [RealmAppLanguage] = appLanguages.map({ + + let realmAppLanguage = RealmAppLanguage() + realmAppLanguage.mapFrom(dataModel: $0) + return realmAppLanguage + }) + + return realmLanguages + } + } + + static func getSampleAppLanguages() -> [AppLanguageCodable] { + + return [ + AppLanguageCodable(languageCode: "am", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "ar", languageDirection: .rightToLeft, languageScriptCode: nil), + AppLanguageCodable(languageCode: "en", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "es", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "fa", languageDirection: .rightToLeft, languageScriptCode: nil), + AppLanguageCodable(languageCode: "he", languageDirection: .rightToLeft, languageScriptCode: nil), + AppLanguageCodable(languageCode: "ja", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "lv", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "pt", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "ru", languageDirection: .leftToRight, languageScriptCode: nil), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hans"), + AppLanguageCodable(languageCode: "zh", languageDirection: .leftToRight, languageScriptCode: "Hant") + ] + } +} diff --git a/godtoolsTests/App/Share/Common/LocaleLanguageName/MockLocaleLanguageName.swift b/godtoolsTests/App/Share/Common/LocaleLanguageName/MockLocaleLanguageName.swift new file mode 100644 index 0000000000..d9776d5c6e --- /dev/null +++ b/godtoolsTests/App/Share/Common/LocaleLanguageName/MockLocaleLanguageName.swift @@ -0,0 +1,39 @@ +// +// MockLocaleLanguageName.swift +// godtools +// +// Created by Levi Eggert on 7/1/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +@testable import godtools + +class MockLocaleLanguageName: LocaleLanguageNameInterface { + + typealias LanguageCode = String + typealias TranslateInLocaleId = String + typealias LanguageName = String + + private let languageNames: [LanguageCode: [TranslateInLocaleId: LanguageName]] + + init(languageNames: [LanguageCode: [TranslateInLocaleId: LanguageName]]) { + + self.languageNames = languageNames + } + + func getLanguageName(forLanguageCode: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? { + + if let translatedInLanguageId = translatedInLanguageId { + + return languageNames[forLanguageCode]?[translatedInLanguageId] + } + + return languageNames[forLanguageCode]?[forLanguageCode] + } + + func getLanguageName(forLocaleId: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? { + + return getLanguageName(forLanguageCode: forLocaleId, translatedInLanguageId: translatedInLanguageId) + } +} diff --git a/godtoolsTests/App/Share/Common/LocaleLanguageRegionName/MockLocaleLanguageRegionName.swift b/godtoolsTests/App/Share/Common/LocaleLanguageRegionName/MockLocaleLanguageRegionName.swift new file mode 100644 index 0000000000..fba36c3628 --- /dev/null +++ b/godtoolsTests/App/Share/Common/LocaleLanguageRegionName/MockLocaleLanguageRegionName.swift @@ -0,0 +1,34 @@ +// +// MockLocaleLanguageRegionName.swift +// godtools +// +// Created by Levi Eggert on 7/1/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +@testable import godtools + +class MockLocaleLanguageRegionName: LocaleLanguageRegionNameInterface { + + typealias RegionCode = String + typealias TranslateInLocaleId = String + typealias RegionName = String + + private let regionNames: [RegionCode: [TranslateInLocaleId: RegionName]] + + init(regionNames: [RegionCode: [TranslateInLocaleId: RegionName]]) { + + self.regionNames = regionNames + } + + func getRegionName(forRegionCode: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? { + + if let translatedInLanguageId = translatedInLanguageId { + + return regionNames[forRegionCode]?[translatedInLanguageId] + } + + return regionNames[forRegionCode]?[forRegionCode] + } +} diff --git a/godtoolsTests/App/Share/Common/LocaleLanguageScriptName/MockLocaleLanguageScriptName.swift b/godtoolsTests/App/Share/Common/LocaleLanguageScriptName/MockLocaleLanguageScriptName.swift new file mode 100644 index 0000000000..0e9229b3a3 --- /dev/null +++ b/godtoolsTests/App/Share/Common/LocaleLanguageScriptName/MockLocaleLanguageScriptName.swift @@ -0,0 +1,34 @@ +// +// MockLocaleLanguageScriptName.swift +// godtools +// +// Created by Levi Eggert on 7/1/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +@testable import godtools + +class MockLocaleLanguageScriptName: LocaleLanguageScriptNameInterface { + + typealias ScriptCode = String + typealias TranslateInLocaleId = String + typealias ScriptName = String + + private let scriptNames: [ScriptCode: [TranslateInLocaleId: ScriptName]] + + init(scriptNames: [ScriptCode: [TranslateInLocaleId: ScriptName]]) { + + self.scriptNames = scriptNames + } + + func getScriptName(forScriptCode: String, translatedInLanguageId: BCP47LanguageIdentifier?) -> String? { + + if let translatedInLanguageId = translatedInLanguageId { + + return scriptNames[forScriptCode]?[translatedInLanguageId] + } + + return scriptNames[forScriptCode]?[forScriptCode] + } +} diff --git a/godtoolsTests/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/GetTranslatedLanguageNameTests.swift b/godtoolsTests/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/GetTranslatedLanguageNameTests.swift new file mode 100644 index 0000000000..e47b4df7fb --- /dev/null +++ b/godtoolsTests/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/GetTranslatedLanguageNameTests.swift @@ -0,0 +1,113 @@ +// +// GetTranslatedLanguageNameTests.swift +// godtoolsTests +// +// Created by Levi Eggert on 7/1/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +@testable import godtools +import Quick +import Nimble + +class GetTranslatedLanguageNameTests: QuickSpec { + + override class func spec() { + + let localizableStrings: [MockLocalizationServices.LocaleId: [MockLocalizationServices.StringKey: String]] = [ + LanguageCodeDomainModel.spanish.value: [ + LanguageCodeDomainModel.english.rawValue: "Inglés", + LanguageCodeDomainModel.french.rawValue: "Francés", + LanguageCodeDomainModel.spanish.rawValue: "Español", + LanguageCodeDomainModel.russian.rawValue: "Ruso" + ] + ] + + let languageNames: [MockLocaleLanguageName.LanguageCode: [MockLocaleLanguageName.TranslateInLocaleId: MockLocaleLanguageName.LanguageName]] = [ + LanguageCodeDomainModel.english.rawValue: [ + LanguageCodeDomainModel.english.rawValue: "English", + LanguageCodeDomainModel.portuguese.rawValue: "Inglês", + LanguageCodeDomainModel.spanish.rawValue: "Inglés", + LanguageCodeDomainModel.russian.rawValue: "Английский" + ], + LanguageCodeDomainModel.french.rawValue: [ + LanguageCodeDomainModel.czech.rawValue: "francouzština", + LanguageCodeDomainModel.english.rawValue: "French", + LanguageCodeDomainModel.portuguese.rawValue: "Francês", + LanguageCodeDomainModel.spanish.rawValue: "Francés", + LanguageCodeDomainModel.russian.rawValue: "Французский" + ], + LanguageCodeDomainModel.spanish.rawValue: [ + LanguageCodeDomainModel.english.rawValue: "Spanish", + LanguageCodeDomainModel.portuguese.rawValue: "Espanhol", + LanguageCodeDomainModel.spanish.rawValue: "Español", + LanguageCodeDomainModel.russian.rawValue: "испанский" + ] + ] + + let getTranslatedLanguageName = GetTranslatedLanguageName( + localizationLanguageNameRepository: MockLocalizationLanguageNameRepository(localizationServices: MockLocalizationServices(localizableStrings: localizableStrings)), + localeLanguageName: MockLocaleLanguageName(languageNames: languageNames), + localeRegionName: MockLocaleLanguageRegionName(regionNames: [:]), + localeScriptName: MockLocaleLanguageScriptName(scriptNames: [:]) + ) + + let frenchLanguage = MockTranslatableLanguage( + languageCode: LanguageCodeDomainModel.french.rawValue, + localeId: LanguageCodeDomainModel.french.rawValue, + fallbackName: "French", + regionCode: nil, + scriptCode: nil + ) + + describe("User is viewing the french language name translated in spanish.") { + + context("If the spanish translation is available in the app bundle's string phrases.") { + + it("Display the language name from the app bundle's string phrases.") { + + let translation: String? = getTranslatedLanguageName.getLanguageName( + language: frenchLanguage, + translatedInLanguage: LanguageCodeDomainModel.spanish.rawValue + ) + + expect(translation).to(equal("Francés")) + } + } + } + + describe("User is viewing the french language name translated in czech.") { + + context("If the czech translation is not available in the app bundle's string phrases, but available in Locale.") { + + it("Display the language name from Locale.") { + + let translation: String? = getTranslatedLanguageName.getLanguageName( + language: frenchLanguage, + translatedInLanguage: LanguageCodeDomainModel.czech.rawValue + ) + + expect(translation).to(equal("francouzština")) + } + } + } + + describe("User is viewing the french language name translated in arabic.") { + + context("If the arabic translation is not available in the app bundle's string phrases and is not available in Locale.") { + + it("Display the fallback name.") { + + let translation: String? = getTranslatedLanguageName.getLanguageName( + language: frenchLanguage, + translatedInLanguage: LanguageCodeDomainModel.arabic.rawValue + ) + + expect(translation).to(equal("French")) + } + } + } + } +} + diff --git a/godtoolsTests/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/MockTranslatableLanguage.swift b/godtoolsTests/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/MockTranslatableLanguage.swift new file mode 100644 index 0000000000..b0bc932afa --- /dev/null +++ b/godtoolsTests/App/Share/Data-DomainInterface/Supporting/GetTranslatedLanguageName/MockTranslatableLanguage.swift @@ -0,0 +1,19 @@ +// +// MockTranslatableLanguage.swift +// godtoolsTests +// +// Created by Levi Eggert on 7/1/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +@testable import godtools + +struct MockTranslatableLanguage: TranslatableLanguage { + + let languageCode: String + let localeId: BCP47LanguageIdentifier + let fallbackName: String + let regionCode: String? + let scriptCode: String? +} diff --git a/godtoolsTests/App/Share/Data/LocalizationLanguageNameRepository/MockLocalizationLanguageNameRepository.swift b/godtoolsTests/App/Share/Data/LocalizationLanguageNameRepository/MockLocalizationLanguageNameRepository.swift new file mode 100644 index 0000000000..a2f33bcf29 --- /dev/null +++ b/godtoolsTests/App/Share/Data/LocalizationLanguageNameRepository/MockLocalizationLanguageNameRepository.swift @@ -0,0 +1,25 @@ +// +// MockLocalizationLanguageNameRepository.swift +// godtoolsTests +// +// Created by Levi Eggert on 7/5/24. +// Copyright © 2024 Cru. All rights reserved. +// + +import Foundation +@testable import godtools + +class MockLocalizationLanguageNameRepository: LocalizationLanguageNameRepositoryInterface { + + private let localizationServices: MockLocalizationServices + + init(localizationServices: MockLocalizationServices) { + + self.localizationServices = localizationServices + } + + func getLanguageName(languageId: BCP47LanguageIdentifier, translatedInLanguage: BCP47LanguageIdentifier) -> String? { + + return localizationServices.stringForLocaleElseEnglish(localeIdentifier: translatedInLanguage, key: languageId) + } +} diff --git a/godtoolsTests/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsBundleLoaderTests.swift b/godtoolsTests/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsBundleLoaderTests.swift deleted file mode 100644 index 7b1174195e..0000000000 --- a/godtoolsTests/App/Share/Data/LocalizationServices/BundleLoader/LocalizableStringsBundleLoaderTests.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// LocalizableStringsBundleLoaderTests.swift -// godtoolsTests -// -// Created by Levi Eggert on 8/9/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation -import XCTest -@testable import godtools - -class LocalizableStringsBundleLoaderTests: XCTestCase { - - static let invalidLocalizableStringsResource: String = "es-419" - - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - private func newBundleLoader() -> LocalizableStringsBundleLoader { - - return LocalizableStringsBundleLoader(localizableStringsFilesBundle: Bundle.main) - } - - func testLoadingEnglishLocalizableStringsBundleExists() { - - let englishStringsBundle: LocalizableStringsBundle? = newBundleLoader().getEnglishBundle(fileType: .strings) - - XCTAssertNotNil(englishStringsBundle) - } - - func testLoadingEnglishLocalizableStringsdictBundleExists() { - - let englishStringsdictBundle: LocalizableStringsBundle? = newBundleLoader().getEnglishBundle(fileType: .stringsdict) - - XCTAssertNotNil(englishStringsdictBundle) - } - - func testLoadingSpanishLocalizableStringsBundleExists() { - - let spanishStringsBundle: LocalizableStringsBundle? = newBundleLoader().bundleForResource(resourceName: "es", fileType: .strings) - - XCTAssertNotNil(spanishStringsBundle) - } - - func testLoadingSpanishLocalizableStringsdictBundleExists() { - - let spanishStringsdictBundle: LocalizableStringsBundle? = newBundleLoader().bundleForResource(resourceName: "es", fileType: .stringsdict) - - XCTAssertNotNil(spanishStringsdictBundle) - } - - func testLoadingLocalizableStringsBundleDoesNotExist() { - - let spanishStringsBundle: LocalizableStringsBundle? = newBundleLoader().bundleForResource(resourceName: LocalizableStringsBundleLoaderTests.invalidLocalizableStringsResource, fileType: .strings) - - XCTAssertNil(spanishStringsBundle) - } - - func testLoadingLocalizableStringsdictBundleDoesNotExist() { - - let spanishStringsdictBundle: LocalizableStringsBundle? = newBundleLoader().bundleForResource(resourceName: LocalizableStringsBundleLoaderTests.invalidLocalizableStringsResource, fileType: .stringsdict) - - XCTAssertNil(spanishStringsdictBundle) - } -} diff --git a/godtoolsTests/App/Share/Data/LocalizationServices/LocalizableStringsRepositoryTests.swift b/godtoolsTests/App/Share/Data/LocalizationServices/LocalizableStringsRepositoryTests.swift deleted file mode 100644 index 5fbbc1a540..0000000000 --- a/godtoolsTests/App/Share/Data/LocalizationServices/LocalizableStringsRepositoryTests.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// LocalizableStringsRepositoryTests.swift -// godtoolsTests -// -// Created by Levi Eggert on 8/9/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation -import XCTest -@testable import godtools - -class LocalizableStringsRepositoryTests: XCTestCase { - - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - private func newStringsRepository(fileType: LocalizableStringsFileType) -> LocalizableStringsRepository { - - return LocalizableStringsRepository( - localizableStringsBundleLoader: LocalizableStringsBundleLoader(localizableStringsFilesBundle: Bundle.main), - fileType: fileType - ) - } - - // MARK: - English - - func testStringInEnglishLocalizableStringsExists() { - - let localizedString: String? = newStringsRepository(fileType: .strings).stringForEnglish(key: "yes") - - XCTAssertNotNil(localizedString) - XCTAssertEqual(localizedString, "Yes") - } - - func testStringInEnglishLocalizableStringsdictExists() { - - let localizedString: String? = newStringsRepository(fileType: .stringsdict).stringForEnglish(key: "badges.toolsOpened") - - XCTAssertNotNil(localizedString) - } - - // MARK: - String For Locale - - func testStringInSpanishLocalizableStringsExists() { - - let localizedString: String? = newStringsRepository(fileType: .strings).stringForLocale(localeIdentifier: "es", key: "yes") - - XCTAssertNotNil(localizedString) - XCTAssertEqual(localizedString, "Sí") - } - - func testStringForLocaleDoesNotExist() { - - let localizedString: String? = newStringsRepository(fileType: .strings).stringForLocale(localeIdentifier: LocalizableStringsBundleLoaderTests.invalidLocalizableStringsResource, key: "yes") - - XCTAssertNil(localizedString) - } - - func testStringForLocaleFallsBackToEnglish() { - - let localizedString: String? = newStringsRepository(fileType: .strings).stringForLocaleElseEnglish(localeIdentifier: LocalizableStringsBundleLoaderTests.invalidLocalizableStringsResource, key: "yes") - - XCTAssertNotNil(localizedString) - XCTAssertEqual(localizedString, "Yes") - } - - func testStringForLocaleFallsBackToSystemElseEnglish() { - - let localizedString: String? = newStringsRepository(fileType: .strings).stringForLocaleElseSystemElseEnglish(localeIdentifier: LocalizableStringsBundleLoaderTests.invalidLocalizableStringsResource, key: "yes") - - XCTAssertNotNil(localizedString) - } -} diff --git a/godtoolsTests/App/Share/Data/LocalizationServices/LocalizationServicesTests.swift b/godtoolsTests/App/Share/Data/LocalizationServices/LocalizationServicesTests.swift deleted file mode 100644 index 05403ecd71..0000000000 --- a/godtoolsTests/App/Share/Data/LocalizationServices/LocalizationServicesTests.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// LocalizationServicesTests.swift -// godtoolsTests -// -// Created by Levi Eggert on 8/9/23. -// Copyright © 2023 Cru. All rights reserved. -// - -import Foundation -import XCTest -@testable import godtools - -class LocalizationServicesTests: XCTestCase { - - private let invalidLocalizableStringsKey: String = UUID().uuidString - - private lazy var localizationServices: LocalizationServices = { - return LocalizationServices(localizableStringsFilesBundle: Bundle.main) - }() - - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testStringInEnglishLocalizableStringsExists() { - - let localizedString: String? = localizationServices.stringForEnglish(key: "yes", fileType: .strings) - - XCTAssertNotNil(localizedString) - XCTAssertEqual(localizedString, "Yes") - } - - func testStringInEnglishLocalizableStringsdictExists() { - - let localizedString: String? = localizationServices.stringForEnglish(key: "badges.toolsOpened", fileType: .stringsdict) - - XCTAssertNotNil(localizedString) - } - - func testMissingStringInEnglishLocalizableStringsdictReturnsKeyValue() { - - let invalidKeyValue: String = "yes" - let localizedString: String = localizationServices.stringForEnglish(key: invalidKeyValue, fileType: .stringsdict) - - XCTAssertEqual(localizedString, invalidKeyValue) - } - - func testMissingStringInLocalizableStringsReturnsKeyValue() { - - let invalidLocalizableStringsResource: String = LocalizableStringsBundleLoaderTests.invalidLocalizableStringsResource - let invalidKeyValue: String = invalidLocalizableStringsKey - - let missingStringForEnglish: String = localizationServices.stringForEnglish(key: invalidKeyValue) - let missingStringForSystemElseEnglish: String = localizationServices.stringForSystemElseEnglish(key: invalidKeyValue) - let missingStringForLocaleElseEnglish: String = localizationServices.stringForLocaleElseEnglish(localeIdentifier: invalidLocalizableStringsResource, key: invalidKeyValue) - let missingStringForLocaleElseSystemElseEnglish: String = localizationServices.stringForLocaleElseSystemElseEnglish(localeIdentifier: invalidLocalizableStringsResource, key: invalidKeyValue) - - XCTAssertEqual(missingStringForEnglish, invalidKeyValue) - XCTAssertEqual(missingStringForSystemElseEnglish, invalidKeyValue) - XCTAssertEqual(missingStringForLocaleElseEnglish, invalidKeyValue) - XCTAssertEqual(missingStringForLocaleElseSystemElseEnglish, invalidKeyValue) - } -} diff --git a/godtoolsTests/App/Share/Data/LocalizationServices/MockLocalizationServices.swift b/godtoolsTests/App/Share/Data/LocalizationServices/MockLocalizationServices.swift index f2eb6593d8..8488841d3e 100644 --- a/godtoolsTests/App/Share/Data/LocalizationServices/MockLocalizationServices.swift +++ b/godtoolsTests/App/Share/Data/LocalizationServices/MockLocalizationServices.swift @@ -8,6 +8,7 @@ import Foundation @testable import godtools +import LocalizationServices class MockLocalizationServices: LocalizationServicesInterface { @@ -21,17 +22,17 @@ class MockLocalizationServices: LocalizationServicesInterface { self.localizableStrings = localizableStrings } - func stringForEnglish(key: String, fileType: LocalizableStringsFileType) -> String { + func stringForEnglish(key: String) -> String { - return stringForLocaleElseEnglish(localeIdentifier: "en", key: key, fileType: fileType) + return stringForLocaleElseEnglish(localeIdentifier: "en", key: key) } - func stringForSystemElseEnglish(key: String, fileType: LocalizableStringsFileType) -> String { + func stringForSystemElseEnglish(key: String) -> String { - return stringForLocaleElseEnglish(localeIdentifier: "en", key: key, fileType: fileType) + return stringForLocaleElseEnglish(localeIdentifier: "en", key: key) } - func stringForLocaleElseEnglish(localeIdentifier: String?, key: String, fileType: LocalizableStringsFileType) -> String { + func stringForLocaleElseEnglish(localeIdentifier: String?, key: String) -> String { guard let localeIdentifier = localeIdentifier else { return "" @@ -44,8 +45,8 @@ class MockLocalizationServices: LocalizationServicesInterface { return localizedStrings[key] ?? "" } - func stringForLocaleElseSystemElseEnglish(localeIdentifier: String?, key: String, fileType: LocalizableStringsFileType) -> String { + func stringForLocaleElseSystemElseEnglish(localeIdentifier: String?, key: String) -> String { - return stringForLocaleElseEnglish(localeIdentifier: localeIdentifier, key: key, fileType: fileType) + return stringForLocaleElseEnglish(localeIdentifier: localeIdentifier, key: key) } }